craft-native 0.0.167 → 0.0.169

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.
@@ -0,0 +1,32 @@
1
+ package {{PACKAGE_NAME}}
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import org.json.JSONArray
7
+
8
+ /**
9
+ * Health Connect left out of this app (`enableHealthConnect` is off), so
10
+ * the native renderer compiles without its library. Every call answers
11
+ * `CAPABILITY_DISABLED`; the `health` capability is not advertised either.
12
+ */
13
+ internal class CraftNativeHealth(
14
+ @Suppress("UNUSED_PARAMETER") context: Context,
15
+ @Suppress("UNUSED_PARAMETER") activityProvider: () -> Activity?,
16
+ @Suppress("UNUSED_PARAMETER") backgroundReads: Boolean,
17
+ @Suppress("UNUSED_PARAMETER") historyReads: Boolean,
18
+ ) {
19
+ fun perform(method: String, args: JSONArray, completion: (CraftNativeCapabilityResult) -> Unit) {
20
+ completion(CraftNativeCapabilityResult(code = "CAPABILITY_DISABLED", message = "Health Connect is not enabled for this app ($method, ${args.length()} arguments)"))
21
+ }
22
+
23
+ fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = false
24
+
25
+ fun close() {}
26
+
27
+ companion object {
28
+ const val PERMISSION_REQUEST_CODE = 8411
29
+ fun readPermissions(type: String): Set<String> = emptySet()
30
+ fun writePermissions(type: String): Set<String> = emptySet()
31
+ }
32
+ }
@@ -0,0 +1,75 @@
1
+ package {{PACKAGE_NAME}}
2
+
3
+ import android.app.NotificationChannel
4
+ import android.app.NotificationManager
5
+ import android.app.PendingIntent
6
+ import android.content.Context
7
+ import android.content.Intent
8
+ import android.os.Build
9
+ import androidx.core.app.NotificationCompat
10
+ import androidx.core.app.NotificationManagerCompat
11
+ import com.google.firebase.messaging.FirebaseMessagingService
12
+ import com.google.firebase.messaging.RemoteMessage
13
+ import org.json.JSONObject
14
+
15
+ /**
16
+ * Firebase Cloud Messaging for the native renderer.
17
+ *
18
+ * - A new registration token reaches every screen and the app script as
19
+ * `PUSH_TOKEN` (`craft.push.onToken`, the `craftPushToken` event), as the
20
+ * iOS host sends a new APNs token.
21
+ * - A notification that arrives while the app is open is shown anyway (FCM
22
+ * leaves that to the app) and heard as `craftNotificationReceived`; a tap
23
+ * opens the app with its data, which the app script hears as
24
+ * `craftNotificationResponse` (MainActivity.notificationTap). In the
25
+ * background, Android shows FCM's notification itself and a tap carries
26
+ * the same data.
27
+ * - A data-only message with `content-available` (a silent push) runs the
28
+ * app script's background work once (`craftSilentPush`).
29
+ */
30
+ class CraftNativeMessagingService : FirebaseMessagingService() {
31
+ override fun onNewToken(token: String) {
32
+ getSharedPreferences("craft-native-push", Context.MODE_PRIVATE).edit().putString("token", token).apply()
33
+ CraftNativeHub.broadcast("PUSH_TOKEN", JSONObject().put("token", token))
34
+ }
35
+
36
+ override fun onMessageReceived(message: RemoteMessage) {
37
+ val data = JSONObject()
38
+ message.data.forEach { (key, value) -> data.put(key, value) }
39
+ val notification = message.notification
40
+ if (notification == null) {
41
+ if (message.data["content-available"] == "1" || message.data["contentAvailable"] == "true") {
42
+ CraftNativeBackground.runOnce(applicationContext, "craftSilentPush")
43
+ }
44
+ return
45
+ }
46
+ CraftNativeHub.broadcast("CRAFT_EVENT", JSONObject().put("event", "craftNotificationReceived").put("detail", data))
47
+ show(notification.title.orEmpty(), notification.body.orEmpty(), message.data)
48
+ }
49
+
50
+ private fun show(title: String, body: String, data: Map<String, String>) {
51
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
52
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && manager.getNotificationChannel(CHANNEL) == null) {
53
+ manager.createNotificationChannel(NotificationChannel(CHANNEL, applicationInfo.loadLabel(packageManager), NotificationManager.IMPORTANCE_DEFAULT))
54
+ }
55
+ val open = Intent(this, MainActivity::class.java)
56
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
57
+ .putExtra("craft.notification", true)
58
+ data.forEach { (key, value) -> open.putExtra(key, value) }
59
+ val id = (data["notification_id"]?.toIntOrNull() ?: System.currentTimeMillis().toInt())
60
+ val tap = PendingIntent.getActivity(this, id, open, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
61
+ val built = NotificationCompat.Builder(this, CHANNEL)
62
+ .setSmallIcon(applicationInfo.icon)
63
+ .setContentTitle(title)
64
+ .setContentText(body)
65
+ .setStyle(NotificationCompat.BigTextStyle().bigText(body))
66
+ .setAutoCancel(true)
67
+ .setContentIntent(tap)
68
+ .build()
69
+ runCatching { NotificationManagerCompat.from(this).notify(id, built) }
70
+ }
71
+
72
+ companion object {
73
+ private const val CHANNEL = "craft-push"
74
+ }
75
+ }
@@ -260,8 +260,20 @@ class CraftNativeMutationDocument {
260
260
  return result
261
261
  }
262
262
 
263
- private fun acceptsChildren(type: String): Boolean =
264
- type == "View" || type == "SafeAreaView" || type == "ScrollView"
263
+ /**
264
+ * Every type the renderer draws children of (MainActivity's makeView):
265
+ * an insert into a pressable row, a modal or a drawing's group was
266
+ * refused as INVALID_PARENT, and the whole screen was drawn again.
267
+ */
268
+ private fun acceptsChildren(type: String): Boolean = type in CONTAINER_TYPES
269
+
270
+ private companion object {
271
+ val CONTAINER_TYPES = setOf(
272
+ "View", "SafeAreaView", "ScrollView", "KeyboardAvoidingView", "Modal", "Pressable",
273
+ "TouchableOpacity", "TouchableHighlight", "ImageBackground", "FlatList", "SectionList",
274
+ "Fragment", "Svg", "G",
275
+ )
276
+ }
265
277
 
266
278
  private fun descendants(id: String, nodes: Map<String, Node>): Set<String> {
267
279
  val result = linkedSetOf<String>()