@craft-native/android 0.0.118 → 0.0.120
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/README.md +12 -4
- package/dist/index.js +6 -2
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/templates/AndroidManifest.xml.template +1 -0
- package/templates/CraftBridge.kt.template +3 -0
- package/templates/CraftNativeCapabilities.kt.template +455 -0
- package/templates/CraftNativeFlatList.kt.template +27 -5
- package/templates/CraftNativeNotificationReceiver.kt.template +43 -0
- package/templates/MainActivityNative.kt.template +229 -47
|
@@ -3,13 +3,17 @@ package {{PACKAGE_NAME}}
|
|
|
3
3
|
import android.content.ClipData
|
|
4
4
|
import android.content.ClipboardManager
|
|
5
5
|
import android.content.Context
|
|
6
|
+
import android.content.Intent
|
|
6
7
|
import android.graphics.BitmapFactory
|
|
7
8
|
import android.graphics.Color
|
|
8
9
|
import android.graphics.Paint
|
|
9
10
|
import android.graphics.Typeface
|
|
10
11
|
import android.graphics.drawable.GradientDrawable
|
|
12
|
+
import android.net.Uri
|
|
11
13
|
import android.os.Build
|
|
12
14
|
import android.os.Bundle
|
|
15
|
+
import android.os.Handler
|
|
16
|
+
import android.os.Looper
|
|
13
17
|
import android.os.VibrationEffect
|
|
14
18
|
import android.os.Vibrator
|
|
15
19
|
import android.text.Editable
|
|
@@ -39,6 +43,7 @@ import androidx.javascriptengine.JavaScriptSandbox
|
|
|
39
43
|
import androidx.javascriptengine.Message
|
|
40
44
|
import androidx.javascriptengine.MessagePort
|
|
41
45
|
import androidx.javascriptengine.MessagePortClient
|
|
46
|
+
import com.google.common.util.concurrent.ListenableFuture
|
|
42
47
|
import org.json.JSONArray
|
|
43
48
|
import org.json.JSONObject
|
|
44
49
|
import java.net.URL
|
|
@@ -46,6 +51,8 @@ import java.util.concurrent.Executors
|
|
|
46
51
|
import java.util.concurrent.Future
|
|
47
52
|
import java.util.UUID
|
|
48
53
|
|
|
54
|
+
private const val CRAFT_NATIVE_CAPABILITY_TIMEOUT_MS = 30_000L
|
|
55
|
+
|
|
49
56
|
private class CraftNativeScrollView(context: Context) : FrameLayout(context) {
|
|
50
57
|
val content = LinearLayout(context)
|
|
51
58
|
private val vertical = ScrollView(context)
|
|
@@ -87,6 +94,9 @@ class MainActivity : AppCompatActivity() {
|
|
|
87
94
|
val flatListOwners = mutableMapOf<String, String>()
|
|
88
95
|
val flatListOwnerByView = mutableMapOf<CraftNativeFlatList, String>()
|
|
89
96
|
val mutations = CraftNativeMutationDocument()
|
|
97
|
+
val capabilityScope = UUID.randomUUID().toString()
|
|
98
|
+
val pendingCapabilities = mutableSetOf<String>()
|
|
99
|
+
val capabilityTimeouts = mutableMapOf<String, Runnable>()
|
|
90
100
|
var active = true
|
|
91
101
|
|
|
92
102
|
fun close() {
|
|
@@ -100,7 +110,16 @@ class MainActivity : AppCompatActivity() {
|
|
|
100
110
|
private var sandbox: JavaScriptSandbox? = null
|
|
101
111
|
private val screens = mutableListOf<Screen>()
|
|
102
112
|
private var bundle = ""
|
|
113
|
+
private lateinit var config: JSONObject
|
|
114
|
+
private lateinit var capabilities: CraftNativeCapabilities
|
|
115
|
+
private val capabilityHandler = Handler(Looper.getMainLooper())
|
|
103
116
|
private var hapticsEnabled = false
|
|
117
|
+
private var clipboardEnabled = false
|
|
118
|
+
internal var nativeAppState = "inactive"
|
|
119
|
+
private set
|
|
120
|
+
private var deliveredInitialDeepLink = false
|
|
121
|
+
private var initialDeepLink: Uri? = null
|
|
122
|
+
private var initialDeepLinkClaimed = false
|
|
104
123
|
private var defaultTextColor = Color.WHITE
|
|
105
124
|
private var useMessagePorts = false
|
|
106
125
|
private val imageExecutor = Executors.newFixedThreadPool(2)
|
|
@@ -110,14 +129,19 @@ class MainActivity : AppCompatActivity() {
|
|
|
110
129
|
|
|
111
130
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
112
131
|
super.onCreate(savedInstanceState)
|
|
132
|
+
initialDeepLink = savedInstanceState?.getString("craft.initialDeepLink")?.let(Uri::parse) ?: intent?.data
|
|
133
|
+
initialDeepLinkClaimed = savedInstanceState?.getBoolean("craft.initialDeepLinkClaimed", false) ?: false
|
|
134
|
+
deliveredInitialDeepLink = savedInstanceState?.getBoolean("craft.deliveredInitialDeepLink", false) ?: false
|
|
113
135
|
setContentView(R.layout.activity_main)
|
|
114
136
|
root = findViewById(R.id.native_root)
|
|
115
|
-
|
|
137
|
+
config = runCatching {
|
|
116
138
|
assets.open("craft.config.json").bufferedReader().use {
|
|
117
139
|
JSONObject(it.readText())
|
|
118
140
|
}
|
|
119
141
|
}.getOrDefault(JSONObject())
|
|
120
142
|
hapticsEnabled = config.optBoolean("enableHaptics", false)
|
|
143
|
+
clipboardEnabled = config.optBoolean("enableClipboard", false)
|
|
144
|
+
capabilities = CraftNativeCapabilities(this, config)
|
|
121
145
|
val background = color(config.optString("backgroundColor", "#1a1a2e"), Color.BLACK)
|
|
122
146
|
root.setBackgroundColor(background)
|
|
123
147
|
defaultTextColor = if (Color.luminance(background) < 0.5) Color.WHITE else Color.BLACK
|
|
@@ -131,29 +155,45 @@ class MainActivity : AppCompatActivity() {
|
|
|
131
155
|
showError("Native STX requires Android JavaScriptSandbox, which is unavailable on this device.")
|
|
132
156
|
return
|
|
133
157
|
}
|
|
134
|
-
val connection =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return@addListener
|
|
141
|
-
}
|
|
142
|
-
useMessagePorts = engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_MESSAGE_PORTS)
|
|
143
|
-
&& engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_PROMISE_RETURN)
|
|
144
|
-
if (!useMessagePorts && !engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING)) {
|
|
145
|
-
engine.close()
|
|
146
|
-
showError("Native STX requires JavaScript message ports or console messaging, unavailable on this device.")
|
|
147
|
-
return@addListener
|
|
148
|
-
}
|
|
149
|
-
Log.i(TAG, "Native STX bridge: ${if (useMessagePorts) "message ports" else "console callback"}")
|
|
150
|
-
sandbox = engine
|
|
151
|
-
openScreen(null)
|
|
152
|
-
} catch (error: Exception) {
|
|
153
|
-
Log.e(TAG, "JavaScriptSandbox connection failed", error)
|
|
154
|
-
showError("Native STX JavaScript engine could not start: ${error.message}")
|
|
158
|
+
val connection = synchronized(SANDBOX_LOCK) {
|
|
159
|
+
sharedSandboxConnection ?: sharedSandbox?.let { existing ->
|
|
160
|
+
sandbox = existing
|
|
161
|
+
null
|
|
162
|
+
} ?: JavaScriptSandbox.createConnectedInstanceAsync(applicationContext).also {
|
|
163
|
+
sharedSandboxConnection = it
|
|
155
164
|
}
|
|
156
|
-
}
|
|
165
|
+
}
|
|
166
|
+
if (connection == null) {
|
|
167
|
+
sandbox?.let(::attachSandbox)
|
|
168
|
+
} else {
|
|
169
|
+
connection.addListener({
|
|
170
|
+
try {
|
|
171
|
+
val engine = connection.get()
|
|
172
|
+
synchronized(SANDBOX_LOCK) {
|
|
173
|
+
sharedSandbox = engine
|
|
174
|
+
}
|
|
175
|
+
if (!isDestroyed) {
|
|
176
|
+
sandbox = engine
|
|
177
|
+
attachSandbox(engine)
|
|
178
|
+
}
|
|
179
|
+
} catch (error: Exception) {
|
|
180
|
+
synchronized(SANDBOX_LOCK) { sharedSandboxConnection = null }
|
|
181
|
+
Log.e(TAG, "JavaScriptSandbox connection failed", error)
|
|
182
|
+
if (!isDestroyed) showError("Native STX JavaScript engine could not start: ${error.message}")
|
|
183
|
+
}
|
|
184
|
+
}, ContextCompat.getMainExecutor(this))
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private fun attachSandbox(engine: JavaScriptSandbox) {
|
|
189
|
+
useMessagePorts = engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_MESSAGE_PORTS)
|
|
190
|
+
&& engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_PROMISE_RETURN)
|
|
191
|
+
if (!useMessagePorts && !engine.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING)) {
|
|
192
|
+
showError("Native STX requires JavaScript message ports or console messaging, unavailable on this device.")
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
Log.i(TAG, "Native STX bridge: ${if (useMessagePorts) "message ports" else "console callback"}")
|
|
196
|
+
openScreen(null)
|
|
157
197
|
}
|
|
158
198
|
|
|
159
199
|
private fun openScreen(route: String?, params: JSONObject = JSONObject()) {
|
|
@@ -170,13 +210,13 @@ class MainActivity : AppCompatActivity() {
|
|
|
170
210
|
val port = if (useMessagePorts) {
|
|
171
211
|
isolate.createMessageChannel("craft", executor, MessagePortClient { message ->
|
|
172
212
|
val current = screen ?: return@MessagePortClient
|
|
173
|
-
if (current.active && message.type == Message.TYPE_STRING)
|
|
213
|
+
if (current.active && message.type == Message.TYPE_STRING) receiveSafely(current, message.string)
|
|
174
214
|
})
|
|
175
215
|
} else {
|
|
176
216
|
isolate.setConsoleCallback(executor, JavaScriptConsoleCallback { entry ->
|
|
177
217
|
val current = screen ?: return@JavaScriptConsoleCallback
|
|
178
218
|
if (current.active && entry.message.startsWith(prefix)) {
|
|
179
|
-
|
|
219
|
+
receiveSafely(current, entry.message.removePrefix(prefix))
|
|
180
220
|
}
|
|
181
221
|
})
|
|
182
222
|
null
|
|
@@ -194,7 +234,12 @@ class MainActivity : AppCompatActivity() {
|
|
|
194
234
|
const port = await android.getNamedPort('craft');
|
|
195
235
|
$routePrelude
|
|
196
236
|
globalThis.__stxNativeBridge = {
|
|
237
|
+
platform: 'android',
|
|
197
238
|
mutationProtocolVersion: 1,
|
|
239
|
+
capabilityProtocolVersion: $CRAFT_NATIVE_CAPABILITY_PROTOCOL_VERSION,
|
|
240
|
+
capabilityTimeoutMs: $CRAFT_NATIVE_CAPABILITY_TIMEOUT_MS,
|
|
241
|
+
capabilities: ${capabilities.names()},
|
|
242
|
+
initialAppState: ${JSONObject.quote(nativeAppState)},
|
|
198
243
|
postMessage: function(value) { port.postMessage(value); },
|
|
199
244
|
onMessage: function(callback) { port.onmessage = function(event) { callback(event.data); }; }
|
|
200
245
|
};
|
|
@@ -202,7 +247,12 @@ class MainActivity : AppCompatActivity() {
|
|
|
202
247
|
""".trimIndent() else """
|
|
203
248
|
$routePrelude
|
|
204
249
|
globalThis.__stxNativeBridge = {
|
|
250
|
+
platform: 'android',
|
|
205
251
|
mutationProtocolVersion: 1,
|
|
252
|
+
capabilityProtocolVersion: $CRAFT_NATIVE_CAPABILITY_PROTOCOL_VERSION,
|
|
253
|
+
capabilityTimeoutMs: $CRAFT_NATIVE_CAPABILITY_TIMEOUT_MS,
|
|
254
|
+
capabilities: ${capabilities.names()},
|
|
255
|
+
initialAppState: ${JSONObject.quote(nativeAppState)},
|
|
206
256
|
postMessage: function(value) { console.log(${JSONObject.quote(prefix)} + value); },
|
|
207
257
|
onMessage: function(callback) { globalThis.__stxNativeCallback = callback; }
|
|
208
258
|
};
|
|
@@ -214,7 +264,18 @@ class MainActivity : AppCompatActivity() {
|
|
|
214
264
|
setup.get()
|
|
215
265
|
val load = isolate.evaluateJavaScriptAsync(bundle)
|
|
216
266
|
load.addListener({
|
|
217
|
-
try {
|
|
267
|
+
try {
|
|
268
|
+
load.get()
|
|
269
|
+
val launchLink = initialDeepLink
|
|
270
|
+
if (!deliveredInitialDeepLink
|
|
271
|
+
&& config.optBoolean("enableDeepLinks")
|
|
272
|
+
&& launchLink != null
|
|
273
|
+
&& screens.lastOrNull() === activeScreen
|
|
274
|
+
) {
|
|
275
|
+
deliveredInitialDeepLink = true
|
|
276
|
+
send(activeScreen, "DEEP_LINK", deepLinkData(launchLink).put("initial", true))
|
|
277
|
+
}
|
|
278
|
+
} catch (error: Exception) {
|
|
218
279
|
Log.e(TAG, "Native STX bundle failed", error)
|
|
219
280
|
if (activeScreen.active) showError("Native STX bundle failed: ${error.message}")
|
|
220
281
|
}
|
|
@@ -232,9 +293,17 @@ class MainActivity : AppCompatActivity() {
|
|
|
232
293
|
root.addView(screen.view)
|
|
233
294
|
}
|
|
234
295
|
|
|
296
|
+
override fun onSaveInstanceState(outState: Bundle) {
|
|
297
|
+
outState.putString("craft.initialDeepLink", initialDeepLink?.toString())
|
|
298
|
+
outState.putBoolean("craft.initialDeepLinkClaimed", initialDeepLinkClaimed)
|
|
299
|
+
outState.putBoolean("craft.deliveredInitialDeepLink", deliveredInitialDeepLink)
|
|
300
|
+
super.onSaveInstanceState(outState)
|
|
301
|
+
}
|
|
302
|
+
|
|
235
303
|
private fun receive(screen: Screen, raw: String) {
|
|
236
304
|
val message = runCatching { JSONObject(raw) }.getOrNull() ?: return
|
|
237
305
|
val payload = message.optJSONObject("payload") ?: JSONObject()
|
|
306
|
+
Log.d(TAG, "Native STX ${message.optString("type")} (${raw.length} chars) for ${screen.route ?: "home"}")
|
|
238
307
|
when (message.optString("type")) {
|
|
239
308
|
"RENDER" -> payload.optJSONObject("document")?.let { render(screen, it) }
|
|
240
309
|
"MUTATE" -> try {
|
|
@@ -270,6 +339,26 @@ class MainActivity : AppCompatActivity() {
|
|
|
270
339
|
}
|
|
271
340
|
"NAVIGATE_BACK" -> if (screens.lastOrNull() === screen) back()
|
|
272
341
|
"API_REQUEST" -> answerAPI(screen, message, payload)
|
|
342
|
+
"API_CANCEL" -> {
|
|
343
|
+
val id = payload.optString("requestId")
|
|
344
|
+
if (id.isNotBlank()) {
|
|
345
|
+
val token = "${screen.capabilityScope}/$id"
|
|
346
|
+
screen.pendingCapabilities.remove(token)
|
|
347
|
+
screen.capabilityTimeouts.remove(token)?.let(capabilityHandler::removeCallbacks)
|
|
348
|
+
capabilities.cancel(token)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private fun receiveSafely(screen: Screen, raw: String) {
|
|
355
|
+
try {
|
|
356
|
+
receive(screen, raw)
|
|
357
|
+
}
|
|
358
|
+
catch (error: Exception) {
|
|
359
|
+
val route = screen.route ?: "home"
|
|
360
|
+
Log.e(TAG, "Native STX message failed for $route", error)
|
|
361
|
+
if (screen.active) showError("Native STX message failed for $route: ${error.message}")
|
|
273
362
|
}
|
|
274
363
|
}
|
|
275
364
|
|
|
@@ -728,34 +817,118 @@ class MainActivity : AppCompatActivity() {
|
|
|
728
817
|
val id = message.optString("id")
|
|
729
818
|
if (id.isBlank()) return
|
|
730
819
|
val args = payload.optJSONArray("args") ?: JSONArray()
|
|
731
|
-
val
|
|
732
|
-
|
|
733
|
-
|
|
820
|
+
val token = "${screen.capabilityScope}/$id"
|
|
821
|
+
screen.pendingCapabilities.add(token)
|
|
822
|
+
val timeout = Runnable {
|
|
823
|
+
if (!screen.active || !screen.pendingCapabilities.remove(token)) return@Runnable
|
|
824
|
+
screen.capabilityTimeouts.remove(token)
|
|
825
|
+
capabilities.cancel(token)
|
|
826
|
+
send(screen, "API_ERROR", JSONObject()
|
|
827
|
+
.put("version", CRAFT_NATIVE_CAPABILITY_PROTOCOL_VERSION)
|
|
828
|
+
.put("requestId", id)
|
|
829
|
+
.put("code", "TIMEOUT")
|
|
830
|
+
.put("message", "Native API request timed out"), id)
|
|
831
|
+
}
|
|
832
|
+
screen.capabilityTimeouts[token] = timeout
|
|
833
|
+
capabilityHandler.postDelayed(timeout, CRAFT_NATIVE_CAPABILITY_TIMEOUT_MS)
|
|
834
|
+
capabilities.perform(
|
|
835
|
+
token,
|
|
836
|
+
payload.optInt("version", CRAFT_NATIVE_CAPABILITY_PROTOCOL_VERSION),
|
|
837
|
+
payload.optString("module"),
|
|
838
|
+
payload.optString("method"),
|
|
839
|
+
args,
|
|
840
|
+
) { result ->
|
|
841
|
+
if (!screen.active || !screen.pendingCapabilities.remove(token)) return@perform
|
|
842
|
+
screen.capabilityTimeouts.remove(token)?.let(capabilityHandler::removeCallbacks)
|
|
843
|
+
val response = JSONObject()
|
|
844
|
+
.put("version", CRAFT_NATIVE_CAPABILITY_PROTOCOL_VERSION)
|
|
845
|
+
.put("requestId", id)
|
|
846
|
+
if (result.code == null) response.put("data", result.data ?: JSONObject.NULL)
|
|
847
|
+
else response.put("code", result.code).put("message", result.message ?: "Native API failed")
|
|
848
|
+
send(screen, if (result.code == null) "API_RESPONSE" else "API_ERROR", response, id)
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
internal fun performImmediateCapability(module: String, method: String, args: JSONArray): CraftNativeCapabilityResult =
|
|
853
|
+
when (module to method) {
|
|
854
|
+
"Device" to "getInfo" -> CraftNativeCapabilityResult(data = JSONObject()
|
|
734
855
|
.put("platform", "android")
|
|
735
856
|
.put("osVersion", Build.VERSION.RELEASE)
|
|
736
857
|
.put("model", Build.MODEL)
|
|
737
|
-
.put("manufacturer", Build.MANUFACTURER)
|
|
738
|
-
|
|
858
|
+
.put("manufacturer", Build.MANUFACTURER)
|
|
859
|
+
.put("deviceId", android.provider.Settings.Secure.getString(contentResolver, android.provider.Settings.Secure.ANDROID_ID))
|
|
860
|
+
.put("isTablet", resources.configuration.smallestScreenWidthDp >= 600)
|
|
861
|
+
.put("screen", JSONObject()
|
|
862
|
+
.put("width", resources.displayMetrics.widthPixels / resources.displayMetrics.density)
|
|
863
|
+
.put("height", resources.displayMetrics.heightPixels / resources.displayMetrics.density)
|
|
864
|
+
.put("scale", resources.displayMetrics.density)))
|
|
865
|
+
"Clipboard" to "write" -> if (!clipboardEnabled) {
|
|
866
|
+
CraftNativeCapabilityResult(code = "CAPABILITY_DISABLED", message = "Clipboard is disabled")
|
|
867
|
+
} else if (args.opt(0) !is String) {
|
|
868
|
+
CraftNativeCapabilityResult(code = "INVALID_ARGUMENT", message = "Clipboard.write needs text")
|
|
869
|
+
} else {
|
|
739
870
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
740
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("", args.
|
|
741
|
-
|
|
871
|
+
clipboard.setPrimaryClip(ClipData.newPlainText("", args.getString(0)))
|
|
872
|
+
CraftNativeCapabilityResult(data = true)
|
|
742
873
|
}
|
|
743
|
-
"Clipboard" to "read" -> {
|
|
874
|
+
"Clipboard" to "read" -> if (!clipboardEnabled) {
|
|
875
|
+
CraftNativeCapabilityResult(code = "CAPABILITY_DISABLED", message = "Clipboard is disabled")
|
|
876
|
+
} else {
|
|
744
877
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
745
|
-
|
|
878
|
+
CraftNativeCapabilityResult(data = clipboard.primaryClip?.getItemAt(0)?.coerceToText(this)?.toString() ?: "")
|
|
746
879
|
}
|
|
747
|
-
"Haptics" to "impact" -> {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
response.put("data", JSONObject().put("ok", true))
|
|
754
|
-
}
|
|
880
|
+
"Haptics" to "impact" -> if (!hapticsEnabled) {
|
|
881
|
+
CraftNativeCapabilityResult(code = "CAPABILITY_DISABLED", message = "Haptics are disabled")
|
|
882
|
+
} else {
|
|
883
|
+
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
|
884
|
+
vibrator.vibrate(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE))
|
|
885
|
+
CraftNativeCapabilityResult(data = true)
|
|
755
886
|
}
|
|
756
|
-
else ->
|
|
887
|
+
else -> CraftNativeCapabilityResult(code = "UNKNOWN_ACTION", message = "Unsupported native API")
|
|
757
888
|
}
|
|
758
|
-
|
|
889
|
+
|
|
890
|
+
internal fun deepLinkData(uri: android.net.Uri): JSONObject = JSONObject()
|
|
891
|
+
.put("url", uri.toString())
|
|
892
|
+
.put("scheme", uri.scheme ?: "")
|
|
893
|
+
.put("host", uri.host ?: "")
|
|
894
|
+
.put("path", uri.path ?: "")
|
|
895
|
+
.put("query", uri.query ?: "")
|
|
896
|
+
.put("queryParams", JSONObject().apply {
|
|
897
|
+
uri.queryParameterNames.forEach { name -> put(name, uri.getQueryParameter(name) ?: "") }
|
|
898
|
+
})
|
|
899
|
+
|
|
900
|
+
private fun publish(type: String, payload: JSONObject) {
|
|
901
|
+
screens.lastOrNull()?.takeIf { it.active }?.let { send(it, type, payload) }
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
override fun onStart() {
|
|
905
|
+
super.onStart()
|
|
906
|
+
nativeAppState = "active"
|
|
907
|
+
publish("APP_STATE", JSONObject().put("state", nativeAppState))
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
override fun onPause() {
|
|
911
|
+
nativeAppState = "inactive"
|
|
912
|
+
publish("APP_STATE", JSONObject().put("state", nativeAppState))
|
|
913
|
+
super.onPause()
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
override fun onStop() {
|
|
917
|
+
nativeAppState = "background"
|
|
918
|
+
publish("APP_STATE", JSONObject().put("state", nativeAppState))
|
|
919
|
+
super.onStop()
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
override fun onNewIntent(intent: Intent) {
|
|
923
|
+
super.onNewIntent(intent)
|
|
924
|
+
setIntent(intent)
|
|
925
|
+
intent.data?.let { publish("DEEP_LINK", deepLinkData(it).put("initial", false)) }
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
internal fun consumeInitialDeepLink(): Uri? {
|
|
929
|
+
if (initialDeepLinkClaimed) return null
|
|
930
|
+
initialDeepLinkClaimed = true
|
|
931
|
+
return initialDeepLink
|
|
759
932
|
}
|
|
760
933
|
|
|
761
934
|
private fun send(screen: Screen, type: String, payload: JSONObject, correlationId: String? = null) {
|
|
@@ -804,6 +977,10 @@ class MainActivity : AppCompatActivity() {
|
|
|
804
977
|
}
|
|
805
978
|
|
|
806
979
|
private fun closeScreen(screen: Screen) {
|
|
980
|
+
screen.capabilityTimeouts.values.forEach(capabilityHandler::removeCallbacks)
|
|
981
|
+
screen.capabilityTimeouts.clear()
|
|
982
|
+
screen.pendingCapabilities.forEach(capabilities::cancel)
|
|
983
|
+
screen.pendingCapabilities.clear()
|
|
807
984
|
releaseViewState(screen, retainedViews(screen).toList())
|
|
808
985
|
screen.controls.clear()
|
|
809
986
|
screen.handlers.clear()
|
|
@@ -833,9 +1010,14 @@ class MainActivity : AppCompatActivity() {
|
|
|
833
1010
|
imageJobs.values.forEach { it.cancel(true) }
|
|
834
1011
|
imageJobs.clear()
|
|
835
1012
|
imageExecutor.shutdownNow()
|
|
836
|
-
|
|
1013
|
+
capabilities.close()
|
|
837
1014
|
super.onDestroy()
|
|
838
1015
|
}
|
|
839
1016
|
|
|
840
|
-
companion object {
|
|
1017
|
+
companion object {
|
|
1018
|
+
private const val TAG = "CraftNativeAndroid"
|
|
1019
|
+
private val SANDBOX_LOCK = Any()
|
|
1020
|
+
private var sharedSandbox: JavaScriptSandbox? = null
|
|
1021
|
+
private var sharedSandboxConnection: ListenableFuture<JavaScriptSandbox>? = null
|
|
1022
|
+
}
|
|
841
1023
|
}
|