@onekeyfe/react-native-chart-webview 3.0.39
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/ChartWebview.podspec +29 -0
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +131 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/chartwebview/ChartWebview.kt +331 -0
- package/android/src/main/java/com/margelo/nitro/chartwebview/ChartWebviewPackage.kt +29 -0
- package/android/src/main/java/com/margelo/nitro/chartwebview/PooledChartWebView.kt +385 -0
- package/ios/ChartWebview.swift +747 -0
- package/lib/module/ChartWebview.nitro.js +4 -0
- package/lib/module/ChartWebview.nitro.js.map +1 -0
- package/lib/module/bridge.js +30 -0
- package/lib/module/bridge.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/lib/nitrogen/generated/android/c++/JHybridChartWebviewSpec.cpp +186 -0
- package/lib/nitrogen/generated/android/c++/JHybridChartWebviewSpec.hpp +88 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.cpp +96 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.hpp +49 -0
- package/lib/nitrogen/generated/android/chartwebview+autolinking.cmake +83 -0
- package/lib/nitrogen/generated/android/chartwebview+autolinking.gradle +27 -0
- package/lib/nitrogen/generated/android/chartwebviewOnLoad.cpp +50 -0
- package/lib/nitrogen/generated/android/chartwebviewOnLoad.hpp +25 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void_std__string.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/HybridChartWebviewSpec.kt +153 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/chartwebviewOnLoad.kt +35 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewManager.kt +50 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewStateUpdater.kt +23 -0
- package/lib/nitrogen/generated/ios/ChartWebview+autolinking.rb +60 -0
- package/lib/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.cpp +49 -0
- package/lib/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.hpp +158 -0
- package/lib/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Umbrella.hpp +46 -0
- package/lib/nitrogen/generated/ios/ChartWebviewAutolinking.mm +33 -0
- package/lib/nitrogen/generated/ios/ChartWebviewAutolinking.swift +25 -0
- package/lib/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.cpp +11 -0
- package/lib/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.hpp +164 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridChartWebviewComponent.mm +146 -0
- package/lib/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/HybridChartWebviewSpec.swift +68 -0
- package/lib/nitrogen/generated/ios/swift/HybridChartWebviewSpec_cxx.swift +450 -0
- package/lib/nitrogen/generated/shared/c++/HybridChartWebviewSpec.cpp +45 -0
- package/lib/nitrogen/generated/shared/c++/HybridChartWebviewSpec.hpp +87 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.cpp +207 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.hpp +118 -0
- package/lib/nitrogen/generated/shared/json/ChartWebviewConfig.json +20 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridChartWebviewSpec.cpp +186 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridChartWebviewSpec.hpp +88 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.cpp +96 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.hpp +49 -0
- package/lib/nitrogen/nitrogen/generated/android/chartwebview+autolinking.cmake +83 -0
- package/lib/nitrogen/nitrogen/generated/android/chartwebview+autolinking.gradle +27 -0
- package/lib/nitrogen/nitrogen/generated/android/chartwebviewOnLoad.cpp +50 -0
- package/lib/nitrogen/nitrogen/generated/android/chartwebviewOnLoad.hpp +25 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void.kt +80 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void_std__string.kt +80 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/HybridChartWebviewSpec.kt +153 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/chartwebviewOnLoad.kt +35 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewManager.kt +50 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewStateUpdater.kt +23 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebview+autolinking.rb +60 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.cpp +49 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.hpp +158 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Umbrella.hpp +46 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebviewAutolinking.mm +33 -0
- package/lib/nitrogen/nitrogen/generated/ios/ChartWebviewAutolinking.swift +25 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.cpp +11 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.hpp +164 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/views/HybridChartWebviewComponent.mm +146 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridChartWebviewSpec.swift +68 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridChartWebviewSpec_cxx.swift +450 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridChartWebviewSpec.cpp +45 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridChartWebviewSpec.hpp +87 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.cpp +207 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.hpp +118 -0
- package/lib/nitrogen/nitrogen/generated/shared/json/ChartWebviewConfig.json +20 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/ChartWebview.nitro.d.ts +21 -0
- package/lib/typescript/src/ChartWebview.nitro.d.ts.map +1 -0
- package/lib/typescript/src/bridge.d.ts +2 -0
- package/lib/typescript/src/bridge.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +145 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitro.json +17 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridChartWebviewSpec.cpp +186 -0
- package/nitrogen/generated/android/c++/JHybridChartWebviewSpec.hpp +88 -0
- package/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.cpp +96 -0
- package/nitrogen/generated/android/c++/views/JHybridChartWebviewStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/chartwebview+autolinking.cmake +83 -0
- package/nitrogen/generated/android/chartwebview+autolinking.gradle +27 -0
- package/nitrogen/generated/android/chartwebviewOnLoad.cpp +50 -0
- package/nitrogen/generated/android/chartwebviewOnLoad.hpp +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/Func_void_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/HybridChartWebviewSpec.kt +153 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/chartwebviewOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewManager.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chartwebview/views/HybridChartWebviewStateUpdater.kt +23 -0
- package/nitrogen/generated/ios/ChartWebview+autolinking.rb +60 -0
- package/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.cpp +49 -0
- package/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Bridge.hpp +158 -0
- package/nitrogen/generated/ios/ChartWebview-Swift-Cxx-Umbrella.hpp +46 -0
- package/nitrogen/generated/ios/ChartWebviewAutolinking.mm +33 -0
- package/nitrogen/generated/ios/ChartWebviewAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridChartWebviewSpecSwift.hpp +164 -0
- package/nitrogen/generated/ios/c++/views/HybridChartWebviewComponent.mm +146 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridChartWebviewSpec.swift +68 -0
- package/nitrogen/generated/ios/swift/HybridChartWebviewSpec_cxx.swift +450 -0
- package/nitrogen/generated/shared/c++/HybridChartWebviewSpec.cpp +45 -0
- package/nitrogen/generated/shared/c++/HybridChartWebviewSpec.hpp +87 -0
- package/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.cpp +207 -0
- package/nitrogen/generated/shared/c++/views/HybridChartWebviewComponent.hpp +118 -0
- package/nitrogen/generated/shared/json/ChartWebviewConfig.json +20 -0
- package/package.json +170 -0
- package/src/ChartWebview.nitro.ts +59 -0
- package/src/bridge.ts +27 -0
- package/src/index.tsx +20 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
package com.margelo.nitro.chartwebview
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.app.Activity
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.graphics.Bitmap
|
|
7
|
+
import android.graphics.Rect
|
|
8
|
+
import android.os.Handler
|
|
9
|
+
import android.os.Looper
|
|
10
|
+
import android.view.PixelCopy
|
|
11
|
+
import android.view.View
|
|
12
|
+
import android.view.ViewGroup
|
|
13
|
+
import android.view.Window
|
|
14
|
+
import android.widget.FrameLayout
|
|
15
|
+
import android.widget.ImageView
|
|
16
|
+
import android.webkit.JavascriptInterface
|
|
17
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
18
|
+
import android.webkit.WebResourceRequest
|
|
19
|
+
import android.webkit.WebResourceResponse
|
|
20
|
+
import android.webkit.WebView
|
|
21
|
+
import androidx.webkit.WebResourceErrorCompat
|
|
22
|
+
import androidx.webkit.WebViewAssetLoader
|
|
23
|
+
import androidx.webkit.WebViewClientCompat
|
|
24
|
+
import androidx.webkit.WebViewCompat
|
|
25
|
+
import androidx.webkit.WebViewFeature
|
|
26
|
+
import org.json.JSONObject
|
|
27
|
+
import java.net.URLEncoder
|
|
28
|
+
import java.util.concurrent.atomic.AtomicInteger
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Owns one real [WebView] together with its message bridge, asset loader and
|
|
32
|
+
* load state. A single instance can be **reparented** across multiple
|
|
33
|
+
* [HybridChartWebview] hosts that share a `reuseKey`, so N mount points are
|
|
34
|
+
* backed by ONE WebView (state preserved, no reload on hand-off).
|
|
35
|
+
*
|
|
36
|
+
* The host currently displaying the WebView is [owner]; all page events
|
|
37
|
+
* (message / load / error) are routed to it. Hosts drive the WebView only
|
|
38
|
+
* through this class — they never touch the [WebView] directly.
|
|
39
|
+
*/
|
|
40
|
+
@SuppressLint("SetJavaScriptEnabled")
|
|
41
|
+
class PooledChartWebView private constructor(
|
|
42
|
+
context: Context,
|
|
43
|
+
val key: String,
|
|
44
|
+
) {
|
|
45
|
+
companion object {
|
|
46
|
+
// Live WebView count, logged on create/destroy. With pooling, N hosts that
|
|
47
|
+
// share a reuseKey must produce exactly ONE "CREATED" line — this is the
|
|
48
|
+
// signal the example uses to verify the singleton.
|
|
49
|
+
private val liveCount = AtomicInteger(0)
|
|
50
|
+
|
|
51
|
+
const val ASSET_HOST = "appassets.androidplatform.net"
|
|
52
|
+
private const val DEFAULT_ENTRY = "index.html"
|
|
53
|
+
|
|
54
|
+
// Tiny platform transport shim: defines the single hook the shared TS bridge
|
|
55
|
+
// (CHART_BRIDGE_JS) calls. The bulk of the bridge lives in the TS layer and
|
|
56
|
+
// arrives via the `bridgeScript` ctor arg — this is the only platform-specific
|
|
57
|
+
// piece (Android -> AndroidChartBridge).
|
|
58
|
+
private const val NATIVE_POST_SHIM_JS =
|
|
59
|
+
"(function(){window.__chartNativePost=function(s){" +
|
|
60
|
+
"try{AndroidChartBridge.postMessage(s);}catch(e){}};})();"
|
|
61
|
+
|
|
62
|
+
fun create(context: Context, key: String): PooledChartWebView =
|
|
63
|
+
PooledChartWebView(context.applicationContext, key)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Shim (defines __chartNativePost) + the TS bridge that uses it. Set lazily via
|
|
67
|
+
// setBridgeScript once the host has the prop value — registering it in init
|
|
68
|
+
// would capture an empty script, since the first reconcile (window-attach) can
|
|
69
|
+
// run before the bridgeScript prop is applied. Re-asserted on page start/finish.
|
|
70
|
+
private var outboundBridgeJs: String = ""
|
|
71
|
+
private var bridgeRegistered = false
|
|
72
|
+
|
|
73
|
+
// Called by the host before the first load. Registers the document-start bridge
|
|
74
|
+
// once a non-empty script is available; subsequent calls are no-ops.
|
|
75
|
+
fun setBridgeScript(bridgeScript: String) {
|
|
76
|
+
if (bridgeRegistered || bridgeScript.isEmpty()) return
|
|
77
|
+
outboundBridgeJs = "$NATIVE_POST_SHIM_JS\n$bridgeScript"
|
|
78
|
+
bridgeRegistered = true
|
|
79
|
+
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
|
|
80
|
+
WebViewCompat.addDocumentStartJavaScript(webView, outboundBridgeJs, setOf("*"))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The host currently displaying this WebView; page events route here. */
|
|
85
|
+
var owner: HybridChartWebview? = null
|
|
86
|
+
|
|
87
|
+
private var assetLoader: WebViewAssetLoader? = null
|
|
88
|
+
private var lastLoadedUrl: String? = null
|
|
89
|
+
private var lastLocalBundle: String? = null
|
|
90
|
+
|
|
91
|
+
// Last rendered frame, used to mask the brief blank frame while the WebView's
|
|
92
|
+
// surface is torn down and recreated during a reparent (move). Kept until
|
|
93
|
+
// clearSnapshot() or the next capture replaces it.
|
|
94
|
+
private var cachedSnapshot: Bitmap? = null
|
|
95
|
+
private var overlay: ImageView? = null
|
|
96
|
+
|
|
97
|
+
// How long the snapshot overlay stays up after a reparent, giving the WebView
|
|
98
|
+
// time to draw its first frame in the new container (~5 frames).
|
|
99
|
+
private val overlayHideDelayMs = 80L
|
|
100
|
+
|
|
101
|
+
val webView: WebView = WebView(context).apply {
|
|
102
|
+
settings.javaScriptEnabled = true
|
|
103
|
+
settings.domStorageEnabled = true
|
|
104
|
+
settings.databaseEnabled = true
|
|
105
|
+
settings.allowFileAccess = false
|
|
106
|
+
settings.allowContentAccess = false
|
|
107
|
+
settings.mediaPlaybackRequiresUserGesture = false
|
|
108
|
+
addJavascriptInterface(ChartBridge(), "AndroidChartBridge")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
init {
|
|
112
|
+
val n = liveCount.incrementAndGet()
|
|
113
|
+
android.util.Log.d("ChartWebviewPool", "WebView CREATED key=$key liveCount=$n")
|
|
114
|
+
|
|
115
|
+
webView.webViewClient = object : WebViewClientCompat() {
|
|
116
|
+
override fun shouldInterceptRequest(
|
|
117
|
+
view: WebView,
|
|
118
|
+
request: WebResourceRequest,
|
|
119
|
+
): WebResourceResponse? = assetLoader?.shouldInterceptRequest(request.url)
|
|
120
|
+
|
|
121
|
+
override fun onPageStarted(view: WebView, url: String?, favicon: android.graphics.Bitmap?) {
|
|
122
|
+
super.onPageStarted(view, url, favicon)
|
|
123
|
+
if (outboundBridgeJs.isNotEmpty()) view.evaluateJavascript(outboundBridgeJs, null)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override fun onPageFinished(view: WebView, url: String?) {
|
|
127
|
+
super.onPageFinished(view, url)
|
|
128
|
+
if (outboundBridgeJs.isNotEmpty()) view.evaluateJavascript(outboundBridgeJs, null)
|
|
129
|
+
owner?.dispatchLoadEnd()
|
|
130
|
+
// Prime the snapshot so the first move already has a frame to mask with.
|
|
131
|
+
refreshSnapshotSoon()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun onReceivedError(
|
|
135
|
+
view: WebView,
|
|
136
|
+
request: WebResourceRequest,
|
|
137
|
+
error: WebResourceErrorCompat,
|
|
138
|
+
) {
|
|
139
|
+
super.onReceivedError(view, request, error)
|
|
140
|
+
if (request.isForMainFrame) {
|
|
141
|
+
owner?.dispatchError(error.description?.toString() ?: "WebView error")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// page -> native: route to the active owner on the UI thread.
|
|
148
|
+
private inner class ChartBridge {
|
|
149
|
+
@JavascriptInterface
|
|
150
|
+
fun postMessage(message: String) {
|
|
151
|
+
runOnUiThread { owner?.dispatchMessage(message) }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Move the WebView into [container], detaching it from any previous parent. */
|
|
156
|
+
fun attachTo(container: ViewGroup) {
|
|
157
|
+
runOnUiThread {
|
|
158
|
+
if (webView.parent === container) return@runOnUiThread
|
|
159
|
+
(webView.parent as? ViewGroup)?.removeView(webView)
|
|
160
|
+
container.addView(
|
|
161
|
+
webView,
|
|
162
|
+
FrameLayout.LayoutParams(
|
|
163
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
164
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
// Mask the reparent's blank frame with the last captured frame, then
|
|
168
|
+
// refresh the cache (async) so the next move has a current frame.
|
|
169
|
+
showSnapshotOverlay(container)
|
|
170
|
+
refreshSnapshotSoon()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Remove the WebView from its current parent (keeps it alive, warm). */
|
|
175
|
+
fun detachFromParent() {
|
|
176
|
+
runOnUiThread {
|
|
177
|
+
// Capture while still attached to the window (PixelCopy needs that), so a
|
|
178
|
+
// release-before-claim ordering still leaves a fresh frame to mask with.
|
|
179
|
+
capturePixelCopy(null)
|
|
180
|
+
removeOverlay()
|
|
181
|
+
(webView.parent as? ViewGroup)?.removeView(webView)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Drop the cached snapshot and remove any visible overlay. */
|
|
186
|
+
fun clearSnapshot() {
|
|
187
|
+
runOnUiThread {
|
|
188
|
+
removeOverlay()
|
|
189
|
+
cachedSnapshot = null
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The last captured frame (shown by inactive hosts as a placeholder). */
|
|
194
|
+
fun snapshot(): Bitmap? = cachedSnapshot
|
|
195
|
+
|
|
196
|
+
/// Capture the current frame a moment after things settle (async).
|
|
197
|
+
fun refreshSnapshotSoon() {
|
|
198
|
+
webView.postDelayed({ capturePixelCopy(null) }, 120)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Capture now and deliver the fresh frame (e.g. so the host that is losing
|
|
202
|
+
/// ownership can update its placeholder to its very last frame).
|
|
203
|
+
fun captureNow(callback: (Bitmap?) -> Unit) {
|
|
204
|
+
capturePixelCopy(callback)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Capture the WebView's REAL on-screen pixels (incl. GPU-rendered chart
|
|
208
|
+
// canvas) via PixelCopy. `webView.draw()` on a software canvas would miss the
|
|
209
|
+
// hardware-accelerated <canvas>, so the chart candles wouldn't be captured.
|
|
210
|
+
// PixelCopy is async and needs the view attached to a window.
|
|
211
|
+
private fun capturePixelCopy(callback: ((Bitmap?) -> Unit)?) {
|
|
212
|
+
// Don't capture mid-reparent (overlay visible): the WebView may be blank for
|
|
213
|
+
// a frame and we'd cache a black image. Keep the last good frame instead.
|
|
214
|
+
if (overlay != null) { callback?.invoke(cachedSnapshot); return }
|
|
215
|
+
val w = webView.width
|
|
216
|
+
val h = webView.height
|
|
217
|
+
if (w <= 0 || h <= 0 || webView.windowToken == null) { callback?.invoke(cachedSnapshot); return }
|
|
218
|
+
val window = currentWindow() ?: run { callback?.invoke(cachedSnapshot); return }
|
|
219
|
+
val location = IntArray(2)
|
|
220
|
+
webView.getLocationInWindow(location)
|
|
221
|
+
val src = Rect(location[0], location[1], location[0] + w, location[1] + h)
|
|
222
|
+
val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
223
|
+
try {
|
|
224
|
+
PixelCopy.request(
|
|
225
|
+
window,
|
|
226
|
+
src,
|
|
227
|
+
bmp,
|
|
228
|
+
{ result ->
|
|
229
|
+
if (result == PixelCopy.SUCCESS) cachedSnapshot = bmp
|
|
230
|
+
callback?.invoke(cachedSnapshot)
|
|
231
|
+
},
|
|
232
|
+
webView.handler ?: Handler(Looper.getMainLooper()),
|
|
233
|
+
)
|
|
234
|
+
} catch (e: Throwable) {
|
|
235
|
+
// Best-effort: a failure just means no flash mask this move.
|
|
236
|
+
callback?.invoke(cachedSnapshot)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// The window owning the container the WebView is attached to (the host
|
|
241
|
+
// Activity). The WebView itself was created with an application context.
|
|
242
|
+
private fun currentWindow(): Window? {
|
|
243
|
+
val ctx = (webView.parent as? View)?.context
|
|
244
|
+
(ctx as? ThemedReactContext)?.currentActivity?.window?.let { return it }
|
|
245
|
+
return (ctx as? Activity)?.window
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private fun showSnapshotOverlay(container: ViewGroup) {
|
|
249
|
+
val snap = cachedSnapshot ?: return
|
|
250
|
+
removeOverlay()
|
|
251
|
+
val iv = ImageView(container.context).apply {
|
|
252
|
+
setImageBitmap(snap)
|
|
253
|
+
scaleType = ImageView.ScaleType.FIT_XY
|
|
254
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
255
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
256
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
container.addView(iv) // added last => drawn on top of the WebView
|
|
260
|
+
overlay = iv
|
|
261
|
+
iv.postDelayed({ removeOverlay() }, overlayHideDelayMs)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun removeOverlay() {
|
|
265
|
+
overlay?.let { (it.parent as? ViewGroup)?.removeView(it) }
|
|
266
|
+
overlay = null
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Apply the source props and (re)load only when the effective URL changes —
|
|
271
|
+
* so reparenting / redundant prop re-applies never reload (which would lose
|
|
272
|
+
* chart state, the whole point of pooling).
|
|
273
|
+
*/
|
|
274
|
+
fun setSource(uri: String?, localBundle: String?, entry: String?, paramsJson: String?) {
|
|
275
|
+
// Never load before the document-start bridge is registered — otherwise the
|
|
276
|
+
// page boots without the bridge and its first $private requests are lost. The
|
|
277
|
+
// host re-calls setSource once the bridgeScript prop arrives.
|
|
278
|
+
if (!bridgeRegistered) return
|
|
279
|
+
if (localBundle != lastLocalBundle) {
|
|
280
|
+
lastLocalBundle = localBundle
|
|
281
|
+
rebuildAssetLoader(localBundle)
|
|
282
|
+
}
|
|
283
|
+
val target = computeTargetUrl(uri, localBundle, entry, paramsJson) ?: return
|
|
284
|
+
if (target == lastLoadedUrl) return
|
|
285
|
+
lastLoadedUrl = target
|
|
286
|
+
runOnUiThread { webView.loadUrl(target) }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
fun postMessage(message: String) {
|
|
290
|
+
val payload = JSONObject.quote(message)
|
|
291
|
+
val js =
|
|
292
|
+
"(function(){try{window.postMessage(JSON.parse($payload), '*');}" +
|
|
293
|
+
"catch(e){window.postMessage($payload, '*');}})();"
|
|
294
|
+
runOnUiThread { webView.evaluateJavascript(js, null) }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
fun reload() {
|
|
298
|
+
runOnUiThread { webView.reload() }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Permanently free the WebView (used for non-pooled / private instances). */
|
|
302
|
+
fun destroy() {
|
|
303
|
+
runOnUiThread {
|
|
304
|
+
(webView.parent as? ViewGroup)?.removeView(webView)
|
|
305
|
+
webView.destroy()
|
|
306
|
+
val n = liveCount.decrementAndGet()
|
|
307
|
+
android.util.Log.d("ChartWebviewPool", "WebView DESTROYED key=$key liveCount=$n")
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private fun rebuildAssetLoader(localBundle: String?) {
|
|
312
|
+
if (localBundle.isNullOrEmpty()) {
|
|
313
|
+
assetLoader = null
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
assetLoader = WebViewAssetLoader.Builder()
|
|
317
|
+
.setDomain(ASSET_HOST)
|
|
318
|
+
.addPathHandler("/", WebViewAssetLoader.AssetsPathHandler(webView.context))
|
|
319
|
+
.build()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private fun computeTargetUrl(
|
|
323
|
+
uri: String?,
|
|
324
|
+
localBundle: String?,
|
|
325
|
+
entry: String?,
|
|
326
|
+
paramsJson: String?,
|
|
327
|
+
): String? {
|
|
328
|
+
if (!uri.isNullOrEmpty()) return uri
|
|
329
|
+
if (localBundle.isNullOrEmpty()) return null
|
|
330
|
+
val entryPath = entry?.takeIf { it.isNotEmpty() } ?: DEFAULT_ENTRY
|
|
331
|
+
val query = buildQueryFromParamsJson(paramsJson)
|
|
332
|
+
return buildString {
|
|
333
|
+
append("https://")
|
|
334
|
+
append(ASSET_HOST)
|
|
335
|
+
append('/')
|
|
336
|
+
append(entryPath)
|
|
337
|
+
if (query.isNotEmpty()) {
|
|
338
|
+
append('?')
|
|
339
|
+
append(query)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private fun buildQueryFromParamsJson(paramsJson: String?): String {
|
|
345
|
+
if (paramsJson.isNullOrEmpty()) return ""
|
|
346
|
+
return try {
|
|
347
|
+
val obj = JSONObject(paramsJson)
|
|
348
|
+
val sb = StringBuilder()
|
|
349
|
+
val keys = obj.keys()
|
|
350
|
+
while (keys.hasNext()) {
|
|
351
|
+
val k = keys.next()
|
|
352
|
+
val rawValue = obj.get(k)
|
|
353
|
+
val value = if (rawValue == JSONObject.NULL) "" else rawValue.toString()
|
|
354
|
+
if (sb.isNotEmpty()) sb.append('&')
|
|
355
|
+
sb.append(encode(k))
|
|
356
|
+
sb.append('=')
|
|
357
|
+
sb.append(encode(value))
|
|
358
|
+
}
|
|
359
|
+
sb.toString()
|
|
360
|
+
} catch (e: Exception) {
|
|
361
|
+
""
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private fun encode(s: String): String =
|
|
366
|
+
URLEncoder.encode(s, "UTF-8").replace("+", "%20")
|
|
367
|
+
|
|
368
|
+
private fun runOnUiThread(action: () -> Unit) {
|
|
369
|
+
if (Looper.myLooper() == Looper.getMainLooper()) action() else webView.post { action() }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Warm pool of [PooledChartWebView]s keyed by `reuseKey`. Entries are created on
|
|
375
|
+
* first use and kept warm (a single static chart instance, OKX-style), so the
|
|
376
|
+
* next mount point that claims the same key reuses the live WebView instead of
|
|
377
|
+
* recreating it. Non-pooled hosts don't use this — they own a private instance.
|
|
378
|
+
*/
|
|
379
|
+
object ChartWebviewPool {
|
|
380
|
+
private val shared = HashMap<String, PooledChartWebView>()
|
|
381
|
+
|
|
382
|
+
@Synchronized
|
|
383
|
+
fun acquireShared(key: String, context: Context): PooledChartWebView =
|
|
384
|
+
shared.getOrPut(key) { PooledChartWebView.create(context, key) }
|
|
385
|
+
}
|