""" Android WebKit 扩展组件 — WebView, WebSettings, WebViewClient, WebChromeClient 等 方法签名完全对齐 Android SDK """ from __future__ import annotations import os import json import threading from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # WebView 常量 # ═══════════════════════════════════════════════════════════════ LOAD_DEFAULT = 0 LOAD_CACHE_ONLY = 1 LOAD_CACHE_ELSE_NETWORK = 2 LOAD_NO_CACHE = 3 LOAD_NORMAL = 0 ERROR_UNKNOWN = -1 ERROR_HOST_LOOKUP = -2 ERROR_UNSUPPORTED_AUTH_SCHEME = -3 ERROR_AUTHENTICATION = -4 ERROR_PROXY_AUTHENTICATION = -5 ERROR_CONNECT = -6 ERROR_IO = -7 ERROR_TIMEOUT = -8 ERROR_REDIRECT_LOOP = -9 ERROR_UNSUPPORTED_SCHEME = -10 ERROR_FAILED_SSL_HANDSHAKE = -11 ERROR_BAD_URL = -12 ERROR_FILE = -13 ERROR_FILE_NOT_FOUND = -14 ERROR_TOO_MANY_REQUESTS = -15 NAVIGATION_TYPE_LINK_CLICKED = 0 NAVIGATION_TYPE_FORM_SUBMITTED = 1 NAVIGATION_TYPE_BACK_FORWARD = 2 NAVIGATION_TYPE_RELOAD = 3 NAVIGATION_TYPE_FORM_RESUBMITTED = 4 NAVIGATION_TYPE_OTHER = -1 OVER_SCROLL_NEVER = 2 OVER_SCROLL_ALWAYS = 0 OVER_SCROLL_IF_CONTENT_SCROLLS = 1 RENDERER_PRIORITY_WAIVED = 0 RENDERER_PRIORITY_BOUND = 1 RENDERER_PRIORITY_IMPORTANT = 2 SCHEME_HTTP = "http" SCHEME_HTTPS = "https" SCHEME_FILE = "file" SCHEME_ABOUT = "about" SCHEME_DATA = "data" SCHEME_JAVASCRIPT = "javascript" WEB_CHROME_CLIENT_GET_DEFAULT_VIDEO_POSTER = 1 WEB_CHROME_CLIENT_GET_VISITED_HISTORY = 2 WEB_RESOURCE_ERROR_STYLE_BOX = 0 WEB_RESOURCE_ERROR_STYLE_GENERIC = 1 # ═══════════════════════════════════════════════════════════════ # WebResourceRequest / WebResourceResponse / WebResourceError # ═══════════════════════════════════════════════════════════════ class WebResourceRequest(Stub): """WebResourceRequest — 资源请求""" def __init__(self, url: str = "", is_for_main_frame: bool = False, is_redirect: bool = False, method: str = "GET", request_headers: Optional[Dict[str, str]] = None, has_user_gesture: bool = False, is_main_frame: bool = False): super().__init__() self._url = url self._is_for_main_frame = is_for_main_frame self._is_redirect = is_redirect self._method = method self._request_headers = request_headers or {} self._has_user_gesture = has_user_gesture self._is_main_frame = is_main_frame def getUrl(self) -> str: return self._url def isForMainFrame(self) -> bool: return self._is_for_main_frame def isRedirect(self) -> bool: return self._is_redirect def getMethod(self) -> str: return self._method def getRequestHeaders(self) -> Dict[str, str]: return dict(self._request_headers) def hasUserGesture(self) -> bool: return self._has_user_gesture def isMainFrame(self) -> bool: return self._is_main_frame def toString(self) -> str: return f"WebResourceRequest(url={self._url}, method={self._method})" class WebResourceResponse(Stub): """WebResourceResponse — 资源响应""" def __init__(self, mime_type: str = "", encoding: str = "", data: Any = None, status_code: int = 200, reason_phrase: str = "OK", response_headers: Optional[Dict[str, str]] = None): super().__init__() self._mime_type = mime_type self._encoding = encoding self._data = data self._status_code = status_code self._reason_phrase = reason_phrase self._response_headers = response_headers or {} def getMimeType(self) -> str: return self._mime_type def getEncoding(self) -> str: return self._encoding def getData(self) -> Any: return self._data def getStatusCode(self) -> int: return self._status_code def getReasonPhrase(self) -> str: return self._reason_phrase def getResponseHeaders(self) -> Dict[str, str]: return dict(self._response_headers) def setMimeType(self, mime_type: str) -> None: self._mime_type = mime_type def setEncoding(self, encoding: str) -> None: self._encoding = encoding def setData(self, data: Any) -> None: self._data = data def setStatusCodeAndReasonPhrase(self, status_code: int, reason: str) -> None: self._status_code = status_code self._reason_phrase = reason def setResponseHeaders(self, headers: Dict[str, str]) -> None: self._response_headers = dict(headers) def toString(self) -> str: return (f"WebResourceResponse(status={self._status_code}, " f"mime={self._mime_type})") class WebResourceError(Stub): """WebResourceError — 资源错误""" ERROR_UNKNOWN = ERROR_UNKNOWN ERROR_HOST_LOOKUP = ERROR_HOST_LOOKUP ERROR_UNSUPPORTED_AUTH_SCHEME = ERROR_UNSUPPORTED_AUTH_SCHEME ERROR_AUTHENTICATION = ERROR_AUTHENTICATION ERROR_PROXY_AUTHENTICATION = ERROR_PROXY_AUTHENTICATION ERROR_CONNECT = ERROR_CONNECT ERROR_IO = ERROR_IO ERROR_TIMEOUT = ERROR_TIMEOUT ERROR_REDIRECT_LOOP = ERROR_REDIRECT_LOOP ERROR_UNSUPPORTED_SCHEME = ERROR_UNSUPPORTED_SCHEME ERROR_FAILED_SSL_HANDSHAKE = ERROR_FAILED_SSL_HANDSHAKE ERROR_BAD_URL = ERROR_BAD_URL ERROR_FILE = ERROR_FILE ERROR_FILE_NOT_FOUND = ERROR_FILE_NOT_FOUND ERROR_TOO_MANY_REQUESTS = ERROR_TOO_MANY_REQUESTS def __init__(self, error_code: int = ERROR_UNKNOWN, description: str = ""): super().__init__() self._error_code = error_code self._description = description def getErrorCode(self) -> int: return self._error_code def getDescription(self) -> str: return self._description def toString(self) -> str: return f"WebResourceError(code={self._error_code}, desc={self._description})" # ═══════════════════════════════════════════════════════════════ # WebMessage / WebMessagePort / WebMessageListener # ═══════════════════════════════════════════════════════════════ class WebMessage(Stub): """WebMessage — Web消息""" def __init__(self, data: str = "", ports: Optional[List["WebMessagePort"]] = None): super().__init__() self._data = data self._ports = list(ports) if ports else [] def getData(self) -> str: return self._data def getPorts(self) -> List["WebMessagePort"]: return list(self._ports) def toString(self) -> str: return f"WebMessage(data={self._data[:50]})" class WebMessagePort(Stub): """WebMessagePort — Web消息端口""" def __init__(self): super().__init__() self._callback: Optional[Callable] = None self._closed = False def postMessage(self, message: WebMessage) -> None: if self._closed: raise RuntimeException("WebMessagePort 已关闭") if self._callback: self._callback(message) def close(self) -> None: self._closed = True self._callback = None def setWebMessageCallback(self, callback: Optional[Callable]) -> None: self._callback = callback def isClosed(self) -> bool: return self._closed def toString(self) -> str: return f"WebMessagePort(closed={self._closed})" class WebMessageListener(Stub): """WebMessageListener — Web消息监听器""" def onPostMessage(self, view: Any, message: WebMessage, source_origin: str, is_main_frame: bool, js_ports: List[WebMessagePort]) -> None: pass # ═══════════════════════════════════════════════════════════════ # WebStorage # ═══════════════════════════════════════════════════════════════ class WebStorage(Stub): """WebStorage — Web存储""" class UsageInfo(Stub): """WebStorage.UsageInfo — 使用信息""" def __init__(self, origin: str = "", usage: int = 0, quota: int = 0): super().__init__() self._origin = origin self._usage = usage self._quota = quota def getOrigin(self) -> str: return self._origin def getUsage(self) -> int: return self._usage def getQuota(self) -> int: return self._quota def toString(self) -> str: return f"UsageInfo(origin={self._origin}, usage={self._usage})" def __init__(self): super().__init__() self._storage: Dict[str, int] = {} self._lock = threading.Lock() def getOrigins(self, callback: Callable) -> None: with self._lock: callback(list(self._storage.keys())) def getUsageForOrigin(self, origin: str, callback: Callable) -> None: with self._lock: callback(self._storage.get(origin, 0)) def getQuotaForOrigin(self, origin: str, callback: Callable) -> None: with self._lock: callback(self._storage.get(origin, 5 * 1024 * 1024)) def setQuotaForOrigin(self, origin: str, quota: int) -> None: with self._lock: self._storage[origin] = quota def deleteOrigin(self, origin: str) -> None: with self._lock: self._storage.pop(origin, None) def deleteAllData(self) -> None: with self._lock: self._storage.clear() def toString(self) -> str: return f"WebStorage(origins={len(self._storage)})" # ═══════════════════════════════════════════════════════════════ # WebView 扩展 # ═══════════════════════════════════════════════════════════════ class WebView(Stub): """WebView — Web视图扩展""" RENDERER_PRIORITY_WAIVED = RENDERER_PRIORITY_WAIVED RENDERER_PRIORITY_BOUND = RENDERER_PRIORITY_BOUND RENDERER_PRIORITY_IMPORTANT = RENDERER_PRIORITY_IMPORTANT SCHEME_HTTP = SCHEME_HTTP SCHEME_HTTPS = SCHEME_HTTPS SCHEME_FILE = SCHEME_FILE SCHEME_ABOUT = SCHEME_ABOUT SCHEME_DATA = SCHEME_DATA SCHEME_JAVASCRIPT = SCHEME_JAVASCRIPT LOAD_DEFAULT = LOAD_DEFAULT LOAD_CACHE_ONLY = LOAD_CACHE_ONLY LOAD_CACHE_ELSE_NETWORK = LOAD_CACHE_ELSE_NETWORK LOAD_NO_CACHE = LOAD_NO_CACHE LOAD_NORMAL = LOAD_NORMAL OVER_SCROLL_NEVER = OVER_SCROLL_NEVER OVER_SCROLL_ALWAYS = OVER_SCROLL_ALWAYS OVER_SCROLL_IF_CONTENT_SCROLLS = OVER_SCROLL_IF_CONTENT_SCROLLS class WebViewTransport(Stub): """WebView.WebViewTransport — WebView传输""" def __init__(self): super().__init__() self._webview: Optional[WebView] = None def setWebView(self, webview: "WebView") -> None: self._webview = webview def getWebView(self) -> Optional["WebView"]: return self._webview def __init__(self, context: Any = None): super().__init__() self._url: Optional[str] = None self._content: Optional[str] = None self._settings: Optional["WebSettings"] = None self._client: Optional["WebViewClient"] = None self._chrome_client: Optional["WebChromeClient"] = None self._web_message_listeners: Dict[str, WebMessageListener] = {} self._renderer_priority: int = self.RENDERER_PRIORITY_BOUND self._renderer_priority_overridden = False self._lock = threading.Lock() def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None) -> None: self._url = url self._content = None if self._client: self._client.onPageStarted(self, url, None) def loadData(self, data: str, mime_type: str = "text/html", encoding: str = "utf-8") -> None: self._content = data self._url = "data:text/html;charset=utf-8," + data[:100] def loadDataWithBaseURL(self, base_url: str, data: str, mime_type: str, encoding: str, history_url: Optional[str]) -> None: self._content = data self._url = base_url def postUrl(self, url: str, post_data: bytes) -> None: self._url = url def getUrl(self) -> Optional[str]: return self._url def getTitle(self) -> str: return "" def getSettings(self) -> "WebSettings": if self._settings is None: self._settings = WebSettings(self) return self._settings def setWebViewClient(self, client: "WebViewClient") -> None: self._client = client def getWebViewClient(self) -> Optional["WebViewClient"]: return self._client def setWebChromeClient(self, client: "WebChromeClient") -> None: self._chrome_client = client def getWebChromeClient(self) -> Optional["WebChromeClient"]: return self._chrome_client def setWebMessageListener(self, js_object_name: str, listener: WebMessageListener) -> None: with self._lock: self._web_message_listeners[js_object_name] = listener def addJavascriptInterface(self, obj: Any, name: str) -> None: pass def removeJavascriptInterface(self, name: str) -> None: pass def evaluateJavascript(self, script: str, callback: Optional[Callable] = None) -> None: pass def evaluateJavascriptForTests(self, script: str, callback: Optional[Callable] = None) -> None: pass def canGoBack(self) -> bool: return False def canGoForward(self) -> bool: return False def goBack(self) -> None: pass def goForward(self) -> None: pass def reload(self) -> None: if self._url: self.loadUrl(self._url) def stopLoading(self) -> None: pass def clearHistory(self) -> None: pass def clearCache(self, include_disk: bool) -> None: pass def clearFormData(self) -> None: pass def destroy(self) -> None: pass def onPause(self) -> None: pass def onResume(self) -> None: pass def isPaused(self) -> bool: return False def pageUp(self, top: bool) -> bool: return True def pageDown(self, bottom: bool) -> bool: return True def setInitialScale(self, scale: int) -> None: pass def getScale(self) -> float: return 1.0 def setRendererPriorityPolicy(self, priority: int, overridden: bool) -> None: self._renderer_priority = priority self._renderer_priority_overridden = overridden def getRendererRequestedPriority(self) -> int: return self._renderer_priority def getRendererPriorityWaivedWhenNotVisible(self) -> bool: return self._renderer_priority_overridden def setNetworkAvailable(self, available: bool) -> None: pass def setLayoutParams(self, params: Any) -> None: pass def toString(self) -> str: return f"WebView(url={self._url})" # ═══════════════════════════════════════════════════════════════ # WebSettings 扩展 # ═══════════════════════════════════════════════════════════════ class WebSettings(Stub): """WebSettings — Web设置扩展""" LOAD_DEFAULT = LOAD_DEFAULT LOAD_CACHE_ONLY = LOAD_CACHE_ONLY LOAD_CACHE_ELSE_NETWORK = LOAD_CACHE_ELSE_NETWORK LOAD_NO_CACHE = LOAD_NO_CACHE LOAD_NORMAL = LOAD_NORMAL MIXED_CONTENT_ALWAYS_ALLOW = 0 MIXED_CONTENT_NEVER_ALLOW = 1 MIXED_CONTENT_COMPATIBILITY_MODE = 2 LAYOUT_ALGORITHM_NORMAL = 0 LAYOUT_ALGORITHM_SINGLE_COLUMN = 1 LAYOUT_ALGORITHM_NARROW_COLUMNS = 2 LAYOUT_ALGORITHM_TEXT_AUTOSIZING = 3 PLUGIN_STATE_ON = 0 PLUGIN_STATE_ON_DEMAND = 1 PLUGIN_STATE_OFF = 2 RENDER_GOOGLE_SUIT = 0 RENDER_PRIORITY_NORMAL = 0 RENDER_PRIORITY_HIGH = 1 RENDER_PRIORITY_LOW = 2 TEXT_AUTOSIZING_TEXT_SIZE_INCREMENT = 0 USER_AGENT_DESKTOP = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" USER_AGENT_MOBILE = "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36" def __init__(self, webview: Optional[WebView] = None): super().__init__() self._webview = webview self._java_script_enabled = False self._java_script_can_open_windows = False self._allow_file_access = True self._allow_file_access_from_file_urls = True self._allow_universal_access_from_file_urls = False self._built_in_zoom_controls = True self._display_zoom_controls = True self._support_zoom = True self._load_with_overview_mode = False self._use_wide_viewport = True self._dom_storage_enabled = True self._database_enabled = True self._app_cache_enabled = True self._cache_mode = LOAD_DEFAULT self._user_agent_string: Optional[str] = None self._default_font_size: int = 16 self._default_fixed_font_size: int = 13 self._minimum_font_size: int = 8 self._minimum_logical_font_size: int = 8 self._text_zoom: int = 100 self._standard_font_family: str = "sans-serif" self._serif_font_family: str = "serif" self._sans_serif_font_family: str = "sans-serif" self._fixed_font_family: str = "monospace" self._cursive_font_family: str = "cursive" self._fantasy_font_family: str = "fantasy" self._default_text_encoding: str = "UTF-8" self._layout_algorithm = self.LAYOUT_ALGORITHM_NORMAL self._block_network_image = False self._block_network_loads = False self._accept_cookies = True self._mixed_content_mode = self.MIXED_CONTENT_NEVER_ALLOW self._offscreen_prefetch_rss = False self._safe_browsing_enabled = True self._save_form_data = True self._save_password = False self._need_initial_focus = True self._support_multiple_windows = False self._allow_content_access = True self._loads_images_automatically = True self._javascript_enabled = False self._media_playback_requires_user_gesture = True def setJavaScriptEnabled(self, enabled: bool) -> None: self._javascript_enabled = enabled def getJavaScriptEnabled(self) -> bool: return self._javascript_enabled def setJavaScriptCanOpenWindowsAutomatically(self, can: bool) -> None: self._java_script_can_open_windows = can def getJavaScriptCanOpenWindowsAutomatically(self) -> bool: return self._java_script_can_open_windows def setAllowFileAccess(self, allow: bool) -> None: self._allow_file_access = allow def getAllowFileAccess(self) -> bool: return self._allow_file_access def setAllowFileAccessFromFileURLs(self, allow: bool) -> None: self._allow_file_access_from_file_urls = allow def getAllowFileAccessFromFileURLs(self) -> bool: return self._allow_file_access_from_file_urls def setAllowUniversalAccessFromFileURLs(self, allow: bool) -> None: self._allow_universal_access_from_file_urls = allow def getAllowUniversalAccessFromFileURLs(self) -> bool: return self._allow_universal_access_from_file_urls def setBuiltInZoomControls(self, enabled: bool) -> None: self._built_in_zoom_controls = enabled def getBuiltInZoomControls(self) -> bool: return self._built_in_zoom_controls def setDisplayZoomControls(self, enabled: bool) -> None: self._display_zoom_controls = enabled def getDisplayZoomControls(self) -> bool: return self._display_zoom_controls def setSupportZoom(self, support: bool) -> None: self._support_zoom = support def supportZoom(self) -> bool: return self._support_zoom def setLoadWithOverviewMode(self, overview: bool) -> None: self._load_with_overview_mode = overview def getLoadWithOverviewMode(self) -> bool: return self._load_with_overview_mode def setUseWideViewPort(self, use: bool) -> None: self._use_wide_viewport = use def getUseWideViewPort(self) -> bool: return self._use_wide_viewport def setDomStorageEnabled(self, enabled: bool) -> None: self._dom_storage_enabled = enabled def getDomStorageEnabled(self) -> bool: return self._dom_storage_enabled def setDatabaseEnabled(self, enabled: bool) -> None: self._database_enabled = enabled def getDatabaseEnabled(self) -> bool: return self._database_enabled def setAppCacheEnabled(self, enabled: bool) -> None: self._app_cache_enabled = enabled def getAppCacheEnabled(self) -> bool: return self._app_cache_enabled def setCacheMode(self, mode: int) -> None: self._cache_mode = mode def getCacheMode(self) -> int: return self._cache_mode def setUserAgentString(self, ua: Optional[str]) -> None: self._user_agent_string = ua def getUserAgentString(self) -> str: return self._user_agent_string or self.USER_AGENT_MOBILE def setDefaultFontSize(self, size: int) -> None: self._default_font_size = max(1, size) def getDefaultFontSize(self) -> int: return self._default_font_size def setDefaultFixedFontSize(self, size: int) -> None: self._default_fixed_font_size = max(1, size) def getDefaultFixedFontSize(self) -> int: return self._default_fixed_font_size def setMinimumFontSize(self, size: int) -> None: self._minimum_font_size = max(1, size) def getMinimumFontSize(self) -> int: return self._minimum_font_size def setTextZoom(self, text_zoom: int) -> None: self._text_zoom = max(50, min(200, text_zoom)) def getTextZoom(self) -> int: return self._text_zoom def setStandardFontFamily(self, font: str) -> None: self._standard_font_family = font def getStandardFontFamily(self) -> str: return self._standard_font_family def setSerifFontFamily(self, font: str) -> None: self._serif_font_family = font def getSerifFontFamily(self) -> str: return self._serif_font_family def setSansSerifFontFamily(self, font: str) -> None: self._sans_serif_font_family = font def getSansSerifFontFamily(self) -> str: return self._sans_serif_font_family def setFixedFontFamily(self, font: str) -> None: self._fixed_font_family = font def getFixedFontFamily(self) -> str: return self._fixed_font_family def setCursiveFontFamily(self, font: str) -> None: self._cursive_font_family = font def getCursiveFontFamily(self) -> str: return self._cursive_font_family def setFantasyFontFamily(self, font: str) -> None: self._fantasy_font_family = font def getFantasyFontFamily(self) -> str: return self._fantasy_font_family def setDefaultTextEncodingName(self, encoding: str) -> None: self._default_text_encoding = encoding def getDefaultTextEncodingName(self) -> str: return self._default_text_encoding def setLayoutAlgorithm(self, algorithm: int) -> None: self._layout_algorithm = algorithm def getLayoutAlgorithm(self) -> int: return self._layout_algorithm def setBlockNetworkImage(self, block: bool) -> None: self._block_network_image = block def getBlockNetworkImage(self) -> bool: return self._block_network_image def setBlockNetworkLoads(self, block: bool) -> None: self._block_network_loads = block def getBlockNetworkLoads(self) -> bool: return self._block_network_loads def setMixedContentMode(self, mode: int) -> None: self._mixed_content_mode = mode def getMixedContentMode(self) -> int: return self._mixed_content_mode def setLoadsImagesAutomatically(self, enabled: bool) -> None: self._loads_images_automatically = enabled def getLoadsImagesAutomatically(self) -> bool: return self._loads_images_automatically def setMediaPlaybackRequiresUserGesture(self, requires: bool) -> None: self._media_playback_requires_user_gesture = requires def getMediaPlaybackRequiresUserGesture(self) -> bool: return self._media_playback_requires_user_gesture def setSupportMultipleWindows(self, support: bool) -> None: self._support_multiple_windows = support def supportMultipleWindows(self) -> bool: return self._support_multiple_windows def setSaveFormData(self, save: bool) -> None: self._save_form_data = save def getSaveFormData(self) -> bool: return self._save_form_data def setSavePassword(self, save: bool) -> None: self._save_password = save def getSavePassword(self) -> bool: return self._save_password def setNeedInitialFocus(self, need: bool) -> None: self._need_initial_focus = need def getNeedInitialFocus(self) -> bool: return self._need_initial_focus def setSafeBrowsingEnabled(self, enabled: bool) -> None: self._safe_browsing_enabled = enabled def getSafeBrowsingEnabled(self) -> bool: return self._safe_browsing_enabled def toString(self) -> str: return (f"WebSettings(js={self._javascript_enabled}, " f"zoom={self._support_zoom})") # ═══════════════════════════════════════════════════════════════ # WebViewClient 扩展 # ═══════════════════════════════════════════════════════════════ class WebViewClient(Stub): """WebViewClient — Web视图客户端扩展""" def __init__(self): super().__init__() def shouldOverrideUrlLoading(self, view: WebView, request: WebResourceRequest) -> bool: return False def shouldOverrideKeyEvent(self, view: WebView, event: Any) -> bool: return False def onPageStarted(self, view: WebView, url: str, favicon: Any) -> None: pass def onPageFinished(self, view: WebView, url: str) -> None: pass def onLoadResource(self, view: WebView, url: str) -> None: pass def onPageCommitVisible(self, view: WebView, url: str) -> None: pass def shouldInterceptRequest(self, view: WebView, request: WebResourceRequest) -> Optional[WebResourceResponse]: return None def onReceivedError(self, view: WebView, request: WebResourceRequest, error: WebResourceError) -> None: pass def onReceivedHttpError(self, view: WebView, request: WebResourceRequest, error_response: WebResourceResponse) -> None: pass def onReceivedHttpAuthRequest(self, view: WebView, handler: Any, host: str, realm: str) -> None: pass def onReceivedLoginRequest(self, view: WebView, realm: str, account: str, args: str) -> None: pass def onReceivedSslError(self, view: WebView, handler: Any, error: Any) -> None: pass def onReceivedClientCertRequest(self, view: WebView, request: Any) -> None: pass def onFormResubmission(self, view: WebView, dont_resend: Any, resend: Any) -> None: pass def doUpdateVisitedHistory(self, view: WebView, url: str, is_reloa: bool) -> None: pass def onUnhandledKeyEvent(self, view: WebView, event: Any) -> None: pass def onScaleChanged(self, view: WebView, old_scale: float, new_scale: float) -> None: pass def onRenderProcessGone(self, view: WebView, detail: Any) -> bool: return True def onUnhandledInputEvent(self, view: WebView, event: Any) -> None: pass def safebrowsingOnBackForwardTransitioned(self, view: WebView, safe: bool) -> None: pass def toString(self) -> str: return "WebViewClient()" # ═══════════════════════════════════════════════════════════════ # WebChromeClient 扩展 # ═══════════════════════════════════════════════════════════════ class WebChromeClient(Stub): """WebChromeClient — Web Chrome客户端扩展""" GET_DEFAULT_VIDEO_POSTER = WEB_CHROME_CLIENT_GET_DEFAULT_VIDEO_POSTER GET_VISITED_HISTORY = WEB_CHROME_CLIENT_GET_VISITED_HISTORY def __init__(self): super().__init__() def onProgressChanged(self, view: WebView, new_progress: int) -> None: pass def onReceivedTitle(self, view: WebView, title: str) -> None: pass def onReceivedIcon(self, view: WebView, icon: Any) -> None: pass def onReceivedTouchIconUrl(self, view: WebView, url: str, precomposed: bool) -> None: pass def onShowCustomView(self, view: Any, callback: Any) -> None: pass def onHideCustomView(self) -> None: pass def onCreateWindow(self, view: WebView, is_dialog: bool, is_user_gesture: bool, result_msg: Any) -> bool: return False def onRequestFocus(self, view: WebView) -> None: pass def onCloseWindow(self, window: WebView) -> None: pass def onJsAlert(self, view: WebView, url: str, message: str, result: Any) -> bool: return False def onJsConfirm(self, view: WebView, url: str, message: str, result: Any) -> bool: return False def onJsPrompt(self, view: WebView, url: str, message: str, default_value: str, result: Any) -> bool: return False def onJsBeforeUnload(self, view: WebView, url: str, message: str, result: Any) -> bool: return False def onConsoleMessage(self, console_message: Any) -> bool: return True def getVideoLoadingProgressView(self) -> Optional[Any]: return None def getDefaultVideoPoster(self) -> Optional[Any]: return None def onShowFileChooser(self, view: WebView, callback: Any, params: Any) -> bool: return False def onPermissionRequest(self, request: Any) -> None: pass def onPermissionRequestCanceled(self, request: Any) -> None: pass def onGeolocationPermissionsShowPrompt(self, origin: str, callback: Any) -> None: pass def onGeolocationPermissionsHidePrompt(self) -> None: pass def onJsTimeout(self) -> bool: return True def onExceededDatabaseQuota(self, url: str, database_id: str, quota: int, estimated: int, quota_updater: Any) -> None: pass def onReachedMaxAppCacheSize(self, space_needed: int, total: int, quota_updater: Any) -> None: pass def onConsoleMessage(self, source_id: str, message: str, line_number: int, msg_type: int) -> bool: return True def toString(self) -> str: return "WebChromeClient()" # ═══════════════════════════════════════════════════════════════ # CookieManager 扩展 # ═══════════════════════════════════════════════════════════════ class CookieManager(Stub): """CookieManager — Cookie管理器扩展""" _instance: Optional["CookieManager"] = None _lock = threading.Lock() def __init__(self): super().__init__() self._cookies: Dict[str, Dict[str, str]] = {} self._accept_cookie = True @staticmethod def getInstance() -> "CookieManager": with CookieManager._lock: if CookieManager._instance is None: CookieManager._instance = CookieManager() return CookieManager._instance def setAcceptCookie(self, accept: bool) -> None: self._accept_cookie = accept def acceptCookie(self) -> bool: return self._accept_cookie def setAcceptThirdPartyCookies(self, webview: WebView, accept: bool) -> None: pass def acceptThirdPartyCookies(self, webview: WebView) -> bool: return False def setCookie(self, url: str, value: str) -> None: domain = self._extract_domain(url) if domain not in self._cookies: self._cookies[domain] = {} if "=" in value: key, val = value.split("=", 1) self._cookies[domain][key.strip()] = val.strip() def getCookie(self, url: str) -> Optional[str]: domain = self._extract_domain(url) if domain in self._cookies: parts = [f"{k}={v}" for k, v in self._cookies[domain].items()] return "; ".join(parts) return None def removeSessionCookies(self) -> None: self._cookies.clear() def removeAllCookies(self) -> None: self._cookies.clear() def removeExpiredCookie(self) -> None: pass def hasCookies(self) -> bool: return bool(self._cookies) def _extract_domain(self, url: str) -> str: if "://" in url: url = url.split("://", 1)[1] return url.split("/")[0].split(":")[0] def flush(self) -> None: pass def toString(self) -> str: return f"CookieManager(domains={len(self._cookies)})" # ═══════════════════════════════════════════════════════════════ # GeolocationPermissions / JsResult / JsPromptResult # ═══════════════════════════════════════════════════════════════ class GeolocationPermissions(Stub): """GeolocationPermissions — 地理位置权限""" _origins: Dict[str, bool] = {} _lock = threading.Lock() @classmethod def getInstance(cls) -> "GeolocationPermissions": return cls() def getOrigins(self, callback: Callable) -> None: with self._lock: callback(list(self._origins.keys())) def getAllowed(self, origin: str, callback: Callable) -> None: with self._lock: callback(origin, self._origins.get(origin, False), False) def clear(self, origin: str) -> None: with self._lock: self._origins.pop(origin, None) def clearAll(self) -> None: with self._lock: self._origins.clear() def allow(self, origin: str, allow: bool) -> None: with self._lock: self._origins[origin] = allow class JsResult(Stub): """JsResult — JavaScript 对话框结果""" def __init__(self): super().__init__() self._result: Optional[bool] = None def confirm(self) -> None: self._result = True def cancel(self) -> None: self._result = False def getResult(self) -> Optional[bool]: return self._result def toString(self) -> str: return f"JsResult(result={self._result})" class JsPromptResult(JsResult): """JsPromptResult — JavaScript 提示框结果""" def __init__(self): super().__init__() self._string_result: str = "" def confirm(self, result: str) -> None: super().confirm() self._string_result = result def getStringResult(self) -> str: return self._string_result def toString(self) -> str: return f"JsPromptResult(result={self._result}, string={self._string_result})" class HttpAuthHandler(Stub): """HttpAuthHandler — HTTP 认证处理器""" def __init__(self): super().__init__() self._handled = False def proceed(self, username: str, password: str) -> None: self._handled = True def cancel(self) -> None: self._handled = True def useHttpAuthUsernamePassword(self, use: bool) -> None: pass def isHandled(self) -> bool: return self._handled def toString(self) -> str: return f"HttpAuthHandler(handled={self._handled})" class SslErrorHandler(Stub): """SslErrorHandler — SSL 错误处理器""" def __init__(self): super().__init__() self._handled = False def proceed(self) -> None: self._handled = True def cancel(self) -> None: self._handled = True def isHandled(self) -> bool: return self._handled def toString(self) -> str: return f"SslErrorHandler(handled={self._handled})" class ClientCertRequest(Stub): """ClientCertRequest — 客户端证书请求""" def __init__(self): super().__init__() self._handled = False def getHost(self) -> str: return "" def getPort(self) -> int: return 443 def getKeyTypes(self) -> List[str]: return ["RSA"] def getPrincipals(self) -> List[str]: return [] def cancel(self) -> None: self._handled = True def proceed(self, private_key: Any, chain: List[Any]) -> None: self._handled = True def ignore(self) -> None: self._handled = True def isHandled(self) -> bool: return self._handled class PermissionRequest(Stub): """PermissionRequest — 权限请求""" RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE" RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE" RESOURCE_PROTECTED_MEDIA_ID = "android.webkit.resource.PROTECTED_MEDIA_ID" RESOURCE_MIDI_SYSEX = "android.webkit.resource.MIDI_SYSEX" def __init__(self): super().__init__() self._origin: Optional[str] = None self._resources: List[str] = [] self._granted = False def getOrigin(self) -> str: return self._origin or "" def getResources(self) -> List[str]: return list(self._resources) def grant(self, resources: List[str]) -> None: self._granted = True def deny(self) -> None: self._granted = False def isGranted(self) -> bool: return self._granted def toString(self) -> str: return f"PermissionRequest(origin={self._origin}, granted={self._granted})" class ConsoleMessage(Stub): """ConsoleMessage — 控制台消息""" MessageLevel = type("MessageLevel", (), { "TIP": 0, "LOG": 1, "WARNING": 2, "ERROR": 3, "DEBUG": 4 }) def __init__(self, message: str = "", source_id: str = "", line_number: int = 0, msg_level: int = MessageLevel.LOG): super().__init__() self._message = message self._source_id = source_id self._line_number = line_number self._msg_level = msg_level def message(self) -> str: return self._message def sourceId(self) -> str: return self._source_id def lineNumber(self) -> int: return self._line_number def messageLevel(self) -> int: return self._msg_level def toString(self) -> str: return f"ConsoleMessage({self._message[:50]})" class ValueCallback(Stub): """ValueCallback — 值回调接口""" def onReceiveValue(self, value: Any) -> None: pass # ═══════════════════════════════════════════════════════════════ # WebBackForwardList / WebHistoryItem / WebIconDatabase / etc. # ═══════════════════════════════════════════════════════════════ class WebBackForwardList(Stub): """WebBackForwardList — 后退/前进列表""" def __init__(self): super().__init__() self._items: List["WebHistoryItem"] = [] self._current_index: int = -1 def getCurrentItem(self) -> Optional["WebHistoryItem"]: if 0 <= self._current_index < len(self._items): return self._items[self._current_index] return None def getCurrentIndex(self) -> int: return self._current_index def getItemAtIndex(self, index: int) -> Optional["WebHistoryItem"]: if 0 <= index < len(self._items): return self._items[index] return None def getSize(self) -> int: return len(self._items) def toString(self) -> str: return f"WebBackForwardList(size={len(self._items)}, current={self._current_index})" class WebHistoryItem(Stub): """WebHistoryItem — Web历史条目""" def __init__(self, url: str = "", title: str = "", favicon: Any = None): super().__init__() self._url = url self._title = title self._favicon = favicon def getUrl(self) -> str: return self._url def getTitle(self) -> str: return self._title def getFavicon(self) -> Optional[Any]: return self._favicon def setUrl(self, url: str) -> None: self._url = url def setTitle(self, title: str) -> None: self._title = title def setFavicon(self, favicon: Any) -> None: self._favicon = favicon def toString(self) -> str: return f"WebHistoryItem(url={self._url}, title={self._title})" class WebIconDatabase(Stub): """WebIconDatabase — Web图标数据库""" _instance: Optional["WebIconDatabase"] = None @staticmethod def getInstance() -> "WebIconDatabase": if WebIconDatabase._instance is None: WebIconDatabase._instance = WebIconDatabase() return WebIconDatabase._instance def open(self, path: str) -> None: pass def close(self) -> None: pass def removeAllIcons(self) -> None: pass def requestIconForPageUrl(self, url: str, callback: Callable) -> None: callback(url, None) def retainIconForPageUrl(self, url: str) -> None: pass def releaseIconForPageUrl(self, url: str) -> None: pass def getIconForPageUrl(self, url: str) -> Optional[Any]: return None def toString(self) -> str: return "WebIconDatabase()" class WebViewDatabase(Stub): """WebViewDatabase — WebView数据库""" _instance: Optional["WebViewDatabase"] = None @staticmethod def getInstance(context: Any) -> "WebViewDatabase": if WebViewDatabase._instance is None: WebViewDatabase._instance = WebViewDatabase() return WebViewDatabase._instance def hasFormData(self) -> bool: return False def clearFormData(self) -> None: pass def hasUsernamePassword(self) -> bool: return False def clearUsernamePassword(self) -> None: pass def hasHttpAuthUsernamePassword(self) -> bool: return False def clearHttpAuthUsernamePassword(self) -> None: pass def toString(self) -> str: return "WebViewDatabase()" class WebViewFragment(Stub): """WebViewFragment — WebView片段""" def __init__(self): super().__init__() self._webview: Optional[WebView] = None def onCreateView(self, inflater: Any, container: Any, savedInstanceState: Any) -> Optional[WebView]: self._webview = WebView() return self._webview def onPause(self) -> None: if self._webview: self._webview.onPause() def onResume(self) -> None: if self._webview: self._webview.onResume() def onDestroyView(self) -> None: if self._webview: self._webview.destroy() def getWebView(self) -> Optional[WebView]: return self._webview def toString(self) -> str: return f"WebViewFragment(webview={'yes' if self._webview else 'no'})" class DateSorter(Stub): """DateSorter — 日期排序器""" DAY_COUNT = 5 def __init__(self, context: Any): super().__init__() self._bounds: List[int] = [0, 0, 0, 0, 0] self._labels: List[str] = ["Today", "Yesterday", "Earlier"] def getBounds(self) -> List[int]: return list(self._bounds) def getLabel(self, index: int) -> str: if 0 <= index < len(self._labels): return self._labels[index] return "" def toString(self) -> str: return "DateSorter()" class FindActionModeCallback(Stub): """FindActionModeCallback — 查找操作模式回调""" def __init__(self, context: Any = None): super().__init__() self._search_text: str = "" self._global_search_count: int = 0 self._match_count: int = 0 def finishActionMode(self) -> None: pass def setText(self, text: str) -> None: self._search_text = text def getText(self) -> str: return self._search_text def setGlobalSearchCount(self, count: int) -> None: self._global_search_count = count def getGlobalSearchCount(self) -> int: return self._global_search_count def setMatchCount(self, count: int) -> None: self._match_count = count def getMatchCount(self) -> int: return self._match_count def toString(self) -> str: return (f"FindActionModeCallback(search={self._search_text}, " f"matches={self._match_count})") class JsDialogHelper(Stub): """JsDialogHelper — JavaScript 对话框助手""" ALERT = 1 CONFIRM = 2 PROMPT = 3 UNLOAD = 4 def __init__(self, result: JsResult, default_value: str = "", dialog_type: int = ALERT, message: str = ""): super().__init__() self._result = result self._default_value = default_value self._dialog_type = dialog_type self._message = message def getDialogType(self) -> int: return self._dialog_type def getMessage(self) -> str: return self._message def getDefaultValue(self) -> str: return self._default_value def invokeCallback(self, result: Any) -> None: if self._result: self._result.confirm() def cancelDialog(self) -> None: if self._result: self._result.cancel() def toString(self) -> str: types = {1: "ALERT", 2: "CONFIRM", 3: "PROMPT", 4: "UNLOAD"} return f"JsDialogHelper(type={types.get(self._dialog_type, 'UNKNOWN')})"