diff --git a/views/build.gradle.kts b/views/build.gradle.kts index 8c78c64a1..47511d15f 100644 --- a/views/build.gradle.kts +++ b/views/build.gradle.kts @@ -55,5 +55,6 @@ dependencies { implementation(libs.androidx.appcompat) implementation(libs.google.android.material) implementation(libs.androidx.glance.appwidget) + implementation(libs.androidx.webkit) } \ No newline at end of file diff --git a/views/src/main/java/com/example/example/snippet/views/webapps/NativeApiAccessJsbridge.kt b/views/src/main/java/com/example/example/snippet/views/webapps/NativeApiAccessJsbridge.kt new file mode 100644 index 000000000..c97648831 --- /dev/null +++ b/views/src/main/java/com/example/example/snippet/views/webapps/NativeApiAccessJsbridge.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.example.snippet.views.webapps + +import android.content.Context +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.webkit.WebMessageCompat +import androidx.webkit.WebViewCompat +import androidx.webkit.WebViewFeature +import java.io.InputStream + +private fun webMessageListenerSnippet(webView: WebView) { + // [START android_views_webapps_web_message_listener] + val myListener = WebViewCompat.WebMessageListener { _, _, _, _, replyProxy -> + // Handle the message from JS + replyProxy.postMessage("Acknowledged!") + } + + // Check whether the WebView version supports the feature. + if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { + val allowedOrigins = setOf("https://www.example.com") + WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener) + } + // [END android_views_webapps_web_message_listener] +} + +// [START android_views_webapps_web_message_binary] +fun setupWebView(webView: WebView) { + if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { + val listener = WebViewCompat.WebMessageListener { view, message, sourceOrigin, isMainFrame, replyProxy -> + + // Check if the received message is an ArrayBuffer + if (message.type == WebMessageCompat.TYPE_ARRAY_BUFFER) { + val binaryData: ByteArray = message.arrayBuffer + // Process your binary data (image, audio, etc.) + println("Received bytes: ${binaryData.size}") + + // Optional: Send a binary reply back to JavaScript. + // This example sends a 3-byte array for simplicity. + if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) { + val replyBytes = byteArrayOf(0x01, 0x02, 0x03) + replyProxy.postMessage(replyBytes) + } + } + } + + // "myBridge" matches the window.myBridge in JavaScript + WebViewCompat.addWebMessageListener( + webView, + "myBridge", + setOf("https://example.com"), // Security: restrict origins + listener + ) + } +} +// [END android_views_webapps_web_message_binary] + +private fun shouldInterceptRequestSnippet(context: Context, webView: WebView) { + // [START android_views_webapps_intercept_request] + webView.webViewClient = object : WebViewClient() { + override fun shouldInterceptRequest( + view: WebView?, + request: WebResourceRequest? + ): WebResourceResponse? { + val url = request?.url ?: return null + + // Check if this is our custom placeholder URL + if (url.host == "app.local" && url.path == "/data/large-file.bin") { + try { + // 1. Get your data as an InputStream + // (from Assets, Files, or a generated byte stream) + val inputStream: InputStream = context.assets.open("my_data.pb") + + // 2. Define Response Headers (Crucial for CORS/Fetch) + val headers = mutableMapOf() + headers["Access-Control-Allow-Origin"] = "*" // Allow fetch from any origin + + // 3. Return the response + return WebResourceResponse( + "application/octet-stream", // MIME type (for example, image/jpeg) + "UTF-8", // Encoding + 200, // Status Code + "OK", // Reason Phrase + headers, // Custom Headers + inputStream // The actual data stream + ) + } catch (e: Exception) { + // Handle exception + } + } + return super.shouldInterceptRequest(view, request) + } + } + // [END android_views_webapps_intercept_request] +}