@craft-native/android 0.0.92 → 0.0.95
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 +13 -1
- package/dist/index.d.ts +111 -0
- package/dist/index.js +702 -0
- package/dist/index.js.map +11 -0
- package/dist/promise-runtime.d.ts +3 -0
- package/package.json +1 -1
- package/templates/CraftBridge.kt.template +271 -72
- package/templates/CraftNative.kt.template +12 -6
- package/templates/MainActivity.kt.template +86 -9
- package/LICENSE.md +0 -21
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
package {{PACKAGE_NAME}}
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
4
5
|
import android.os.Bundle
|
|
5
6
|
import android.content.Intent
|
|
6
7
|
import android.graphics.Bitmap
|
|
8
|
+
import android.net.ConnectivityManager
|
|
9
|
+
import android.net.Network
|
|
7
10
|
import android.net.Uri
|
|
11
|
+
import android.util.Log
|
|
8
12
|
import android.webkit.WebResourceRequest
|
|
9
13
|
import android.webkit.WebResourceError
|
|
10
14
|
import android.webkit.WebResourceResponse
|
|
@@ -19,6 +23,20 @@ import org.json.JSONObject
|
|
|
19
23
|
import java.io.FileNotFoundException
|
|
20
24
|
import java.net.URLConnection
|
|
21
25
|
|
|
26
|
+
/** Main-frame failures for which the bundled app is an honest fallback (#254). */
|
|
27
|
+
object CraftLoadFailure {
|
|
28
|
+
fun isUnreachable(errorCode: Int): Boolean = when (errorCode) {
|
|
29
|
+
WebViewClient.ERROR_HOST_LOOKUP,
|
|
30
|
+
WebViewClient.ERROR_CONNECT,
|
|
31
|
+
WebViewClient.ERROR_TIMEOUT,
|
|
32
|
+
// Android uses ERROR_IO for connection interruptions as well as local
|
|
33
|
+
// I/O failures. Keep it here deliberately: it is the closest Android
|
|
34
|
+
// equivalent to iOS's NSURLErrorNetworkConnectionLost.
|
|
35
|
+
WebViewClient.ERROR_IO -> true
|
|
36
|
+
else -> false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
class MainActivity : AppCompatActivity() {
|
|
23
41
|
private lateinit var webView: WebView
|
|
24
42
|
private lateinit var craftBridge: CraftBridge
|
|
@@ -32,6 +50,28 @@ class MainActivity : AppCompatActivity() {
|
|
|
32
50
|
.build()
|
|
33
51
|
}
|
|
34
52
|
private var loadedBundledFallback = false
|
|
53
|
+
private lateinit var connectivityManager: ConnectivityManager
|
|
54
|
+
@Volatile
|
|
55
|
+
private var isConnected = false
|
|
56
|
+
private var networkMonitoringRegistered = false
|
|
57
|
+
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
|
58
|
+
override fun onAvailable(network: Network) {
|
|
59
|
+
val wasConnected = isConnected
|
|
60
|
+
isConnected = true
|
|
61
|
+
if (!wasConnected && isConnected) {
|
|
62
|
+
runOnUiThread {
|
|
63
|
+
returnFromBundledFallback("the network came back")
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun onLost(network: Network) {
|
|
69
|
+
// A second transport can already be the default by the time the
|
|
70
|
+
// old one reports its loss, so read current state instead of
|
|
71
|
+
// blindly turning the latch off.
|
|
72
|
+
isConnected = connectivityManager.activeNetwork != null
|
|
73
|
+
}
|
|
74
|
+
}
|
|
35
75
|
|
|
36
76
|
@SuppressLint("SetJavaScriptEnabled")
|
|
37
77
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
@@ -46,21 +86,29 @@ class MainActivity : AppCompatActivity() {
|
|
|
46
86
|
craftBridge = CraftBridge(this, webView, CraftHealthConnect(this, webView))
|
|
47
87
|
// The link that launched the app (#215). Queued until the page's bridge
|
|
48
88
|
// is injected, then dispatched once with `initial: true`, so a page that
|
|
49
|
-
// subscribes rather than calling getInitialURL still hears it.
|
|
50
|
-
//
|
|
51
|
-
// A
|
|
52
|
-
//
|
|
89
|
+
// subscribes rather than calling getInitialURL still hears it.
|
|
90
|
+
//
|
|
91
|
+
// A relaunch from Recents carries the intent the app was first opened
|
|
92
|
+
// with. The person picked the task out of a list, not the link or the
|
|
93
|
+
// shortcut inside it, and neither is news the second time — so both
|
|
94
|
+
// are skipped, not just the link (#229).
|
|
95
|
+
//
|
|
96
|
+
// A recreated activity restores what the bridge saved rather than
|
|
97
|
+
// reading its intent, which onNewIntent may have replaced with a
|
|
98
|
+
// later warm link.
|
|
99
|
+
val launchedFromHistory = intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0
|
|
53
100
|
if (savedInstanceState == null) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
101
|
+
if (!launchedFromHistory) {
|
|
102
|
+
intent.data?.toString()?.let(craftBridge::receiveDeepLink)
|
|
103
|
+
handleIncomingShortcut(intent)
|
|
104
|
+
}
|
|
57
105
|
} else {
|
|
58
106
|
craftBridge.restoreDeepLinks(savedInstanceState)
|
|
59
107
|
}
|
|
60
|
-
if (savedInstanceState == null) handleIncomingShortcut(intent)
|
|
61
108
|
|
|
62
109
|
setupWebView()
|
|
63
110
|
loadContent()
|
|
111
|
+
setupNetworkMonitoring()
|
|
64
112
|
}
|
|
65
113
|
|
|
66
114
|
@SuppressLint("SetJavaScriptEnabled")
|
|
@@ -106,7 +154,13 @@ class MainActivity : AppCompatActivity() {
|
|
|
106
154
|
|
|
107
155
|
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
|
|
108
156
|
super.onReceivedError(view, request, error)
|
|
109
|
-
if (
|
|
157
|
+
if (
|
|
158
|
+
request?.isForMainFrame == true &&
|
|
159
|
+
error != null &&
|
|
160
|
+
CraftLoadFailure.isUnreachable(error.errorCode) &&
|
|
161
|
+
hasBundledFallback &&
|
|
162
|
+
!loadedBundledFallback
|
|
163
|
+
) {
|
|
110
164
|
loadedBundledFallback = true
|
|
111
165
|
webView.loadUrl(BUNDLED_APP_URL)
|
|
112
166
|
}
|
|
@@ -119,6 +173,23 @@ class MainActivity : AppCompatActivity() {
|
|
|
119
173
|
webView.addJavascriptInterface(craftBridge, "CraftAndroid")
|
|
120
174
|
}
|
|
121
175
|
|
|
176
|
+
private fun setupNetworkMonitoring() {
|
|
177
|
+
if (!hasBundledFallback || devServerUrl == null) return
|
|
178
|
+
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
179
|
+
isConnected = connectivityManager.activeNetwork != null
|
|
180
|
+
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
|
181
|
+
networkMonitoringRegistered = true
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Leave the bundled copy for the configured remote origin again. */
|
|
185
|
+
private fun returnFromBundledFallback(reason: String) {
|
|
186
|
+
val remoteUrl = devServerUrl ?: return
|
|
187
|
+
if (!loadedBundledFallback) return
|
|
188
|
+
loadedBundledFallback = false
|
|
189
|
+
Log.i(TAG, "$reason; loading $remoteUrl again instead of the bundled copy")
|
|
190
|
+
webView.loadUrl(remoteUrl)
|
|
191
|
+
}
|
|
192
|
+
|
|
122
193
|
private fun loadContent() {
|
|
123
194
|
// Check for dev server URL in config
|
|
124
195
|
val remoteUrl = devServerUrl
|
|
@@ -227,6 +298,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
227
298
|
override fun onResume() {
|
|
228
299
|
super.onResume()
|
|
229
300
|
webView.onResume()
|
|
301
|
+
returnFromBundledFallback("the app returned to the foreground")
|
|
230
302
|
}
|
|
231
303
|
|
|
232
304
|
override fun onPause() {
|
|
@@ -235,6 +307,10 @@ class MainActivity : AppCompatActivity() {
|
|
|
235
307
|
}
|
|
236
308
|
|
|
237
309
|
override fun onDestroy() {
|
|
310
|
+
if (networkMonitoringRegistered) {
|
|
311
|
+
connectivityManager.unregisterNetworkCallback(networkCallback)
|
|
312
|
+
networkMonitoringRegistered = false
|
|
313
|
+
}
|
|
238
314
|
craftBridge.close()
|
|
239
315
|
webView.destroy()
|
|
240
316
|
super.onDestroy()
|
|
@@ -256,6 +332,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
256
332
|
}
|
|
257
333
|
|
|
258
334
|
companion object {
|
|
335
|
+
private const val TAG = "CraftMainActivity"
|
|
259
336
|
private const val BUNDLED_APP_HOST = "appassets.androidplatform.net"
|
|
260
337
|
private const val BUNDLED_APP_ORIGIN = "https://appassets.androidplatform.net"
|
|
261
338
|
private const val BUNDLED_APP_URL = "$BUNDLED_APP_ORIGIN/"
|
package/LICENSE.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Stacks.js
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|