Skip to content

Commit 4124ba9

Browse files
committed
feat: add RiveLogger with configurable JS handler and deprecation warnings
Adds a general-purpose logging system (RiveLogger) that surfaces native logs to JS. By default logs show in the RN console via console.error/warn/log. Users can set a custom handler to filter, suppress, or forward logs (e.g. to Sentry). On Android, Rive C++ runtime logs are also unified through RiveLog. Deprecated blocking methods now emit once-per-session warnings via the DeprecationWarning utility.
1 parent 5c7cd87 commit 4124ba9

48 files changed

Lines changed: 1120 additions & 41 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.margelo.nitro.rive
2+
3+
object DeprecationWarning {
4+
private val warned = mutableSetOf<String>()
5+
6+
fun warn(method: String, replacement: String) {
7+
if (warned.add(method)) {
8+
RiveLog.w("Deprecation",
9+
"'$method' is deprecated and blocks the calling thread. Use '$replacement' instead.")
10+
}
11+
}
12+
}

android/src/new/java/com/margelo/nitro/rive/HybridRiveFile.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ class HybridRiveFile(
2626
// Deprecated: Use getViewModelNamesAsync instead
2727
override val viewModelCount: Double?
2828
get() {
29+
DeprecationWarning.warn("viewModelCount", "getViewModelNamesAsync")
2930
val file = riveFile ?: return null
3031
return try {
3132
runBlocking { file.getViewModelNames() }.size.toDouble()
3233
} catch (e: Exception) {
33-
Log.e(TAG, "viewModelCount failed", e)
34+
RiveLog.e(TAG, "viewModelCount failed: ${e.message}")
3435
null
3536
}
3637
}
@@ -44,14 +45,15 @@ class HybridRiveFile(
4445

4546
// Deprecated: Use getViewModelNamesAsync + viewModelByNameAsync instead
4647
override fun viewModelByIndex(index: Double): HybridViewModelSpec? {
48+
DeprecationWarning.warn("viewModelByIndex", "getViewModelNamesAsync + viewModelByNameAsync")
4749
val file = riveFile ?: return null
4850
return try {
4951
val names = runBlocking { file.getViewModelNames() }
5052
val idx = index.toInt()
5153
if (idx < 0 || idx >= names.size) return null
5254
HybridViewModel(file, riveWorker, names[idx], this, ViewModelSource.Named(names[idx]))
5355
} catch (e: Exception) {
54-
Log.e(TAG, "viewModelByIndex($index) failed", e)
56+
RiveLog.e(TAG, "viewModelByIndex($index) failed: ${e.message}")
5557
null
5658
}
5759
}
@@ -67,10 +69,11 @@ class HybridRiveFile(
6769

6870
// Deprecated: Use viewModelByNameAsync instead
6971
override fun viewModelByName(name: String): HybridViewModelSpec? {
72+
DeprecationWarning.warn("viewModelByName", "viewModelByNameAsync")
7073
return try {
7174
runBlocking { viewModelByNameImpl(name, validate = true) }
7275
} catch (e: Exception) {
73-
Log.e(TAG, "viewModelByName('$name') failed", e)
76+
RiveLog.e(TAG, "viewModelByName('$name') failed: ${e.message}")
7477
null
7578
}
7679
}
@@ -105,10 +108,11 @@ class HybridRiveFile(
105108

106109
// Deprecated: Use defaultArtboardViewModelAsync instead
107110
override fun defaultArtboardViewModel(artboardBy: ArtboardBy?): HybridViewModelSpec? {
111+
DeprecationWarning.warn("defaultArtboardViewModel", "defaultArtboardViewModelAsync")
108112
return try {
109113
runBlocking { defaultArtboardViewModelImpl(artboardBy) }
110114
} catch (e: Exception) {
111-
Log.e(TAG, "defaultArtboardViewModel failed", e)
115+
RiveLog.e(TAG, "defaultArtboardViewModel failed: ${e.message}")
112116
null
113117
}
114118
}
@@ -120,11 +124,12 @@ class HybridRiveFile(
120124
// Deprecated: Use getArtboardCountAsync instead
121125
override val artboardCount: Double
122126
get() {
127+
DeprecationWarning.warn("artboardCount", "getArtboardCountAsync")
123128
val file = riveFile ?: return 0.0
124129
return try {
125130
runBlocking { file.getArtboardNames() }.size.toDouble()
126131
} catch (e: Exception) {
127-
Log.e(TAG, "artboardCount failed", e)
132+
RiveLog.e(TAG, "artboardCount failed: ${e.message}")
128133
0.0
129134
}
130135
}
@@ -139,11 +144,12 @@ class HybridRiveFile(
139144
// Deprecated: Use getArtboardNamesAsync instead
140145
override val artboardNames: Array<String>
141146
get() {
147+
DeprecationWarning.warn("artboardNames", "getArtboardNamesAsync")
142148
val file = riveFile ?: return emptyArray()
143149
return try {
144150
runBlocking { file.getArtboardNames() }.toTypedArray()
145151
} catch (e: Exception) {
146-
Log.e(TAG, "artboardNames failed", e)
152+
RiveLog.e(TAG, "artboardNames failed: ${e.message}")
147153
emptyArray()
148154
}
149155
}

android/src/new/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ import kotlinx.coroutines.Dispatchers
1616
import kotlinx.coroutines.withContext
1717

1818
/**
19-
* Custom RiveLog logger that logs to Logcat and broadcasts error messages
20-
* to registered listeners. This captures C++ errors from the Rive CommandQueue
21-
* (e.g., "State machine not found", "Draw failed") that are otherwise silent.
19+
* Custom RiveLog logger that routes all Rive C++ runtime logs through [RiveLog]
20+
* and broadcasts error messages to registered listeners. This captures C++ errors
21+
* from the Rive CommandQueue (e.g., "State machine not found", "Draw failed").
2222
*/
2323
object RiveErrorLogger : app.rive.RiveLog.Logger {
24-
private val logcat = app.rive.RiveLog.LogcatLogger()
2524
private val listeners = mutableListOf<(String) -> Unit>()
2625
private val reportedErrors = mutableSetOf<String>()
2726

@@ -47,13 +46,13 @@ object RiveErrorLogger : app.rive.RiveLog.Logger {
4746
synchronized(reportedErrors) { reportedErrors.clear() }
4847
}
4948

50-
override fun v(tag: String, msg: () -> String) = logcat.v(tag, msg)
51-
override fun d(tag: String, msg: () -> String) = logcat.d(tag, msg)
52-
override fun i(tag: String, msg: () -> String) = logcat.i(tag, msg)
53-
override fun w(tag: String, msg: () -> String) = logcat.w(tag, msg)
49+
override fun v(tag: String, msg: () -> String) { RiveLog.d(tag, msg()) }
50+
override fun d(tag: String, msg: () -> String) { RiveLog.d(tag, msg()) }
51+
override fun i(tag: String, msg: () -> String) { RiveLog.i(tag, msg()) }
52+
override fun w(tag: String, msg: () -> String) { RiveLog.w(tag, msg()) }
5453
override fun e(tag: String, t: Throwable?, msg: () -> String) {
5554
val message = msg()
56-
logcat.e(tag, t) { message }
55+
RiveLog.e(tag, message)
5756
broadcastError(tag, message)
5857
}
5958
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.margelo.nitro.rive
2+
3+
import androidx.annotation.Keep
4+
import com.facebook.proguard.annotations.DoNotStrip
5+
6+
@Keep
7+
@DoNotStrip
8+
class HybridRiveLogger : HybridRiveLoggerSpec() {
9+
override fun setHandler(handler: (level: String, tag: String, message: String) -> Unit) {
10+
RiveLog.handler = handler
11+
}
12+
13+
override fun resetHandler() {
14+
RiveLog.handler = null
15+
}
16+
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModel.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,24 @@ class HybridViewModel(
3333

3434
override val propertyCount: Double
3535
get() {
36+
DeprecationWarning.warn("propertyCount", "getPropertyCountAsync")
3637
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
3738
return try {
3839
runBlocking { riveFile.getViewModelProperties(name) }.size.toDouble()
3940
} catch (e: Exception) {
40-
Log.e(TAG, "propertyCount failed", e)
41+
RiveLog.e(TAG, "propertyCount failed: ${e.message}")
4142
0.0
4243
}
4344
}
4445

4546
override val instanceCount: Double
4647
get() {
48+
DeprecationWarning.warn("instanceCount", "getInstanceCountAsync")
4749
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
4850
return try {
4951
runBlocking { riveFile.getViewModelInstanceNames(name) }.size.toDouble()
5052
} catch (e: Exception) {
51-
Log.e(TAG, "instanceCount failed", e)
53+
RiveLog.e(TAG, "instanceCount failed: ${e.message}")
5254
0.0
5355
}
5456
}
@@ -68,6 +70,7 @@ class HybridViewModel(
6870

6971
// Deprecated: Use createInstanceByNameAsync instead
7072
override fun createInstanceByIndex(index: Double): HybridViewModelInstanceSpec? {
73+
DeprecationWarning.warn("createInstanceByIndex", "createInstanceByNameAsync")
7174
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
7275
return try {
7376
val idx = index.toInt()
@@ -78,7 +81,7 @@ class HybridViewModel(
7881
} catch (e: UnsupportedOperationException) {
7982
throw e
8083
} catch (e: Exception) {
81-
Log.e(TAG, "createInstanceByIndex($index) failed", e)
84+
RiveLog.e(TAG, "createInstanceByIndex($index) failed: ${e.message}")
8285
null
8386
}
8487
}
@@ -94,13 +97,14 @@ class HybridViewModel(
9497

9598
// Deprecated: Use createInstanceByNameAsync instead
9699
override fun createInstanceByName(name: String): HybridViewModelInstanceSpec? {
100+
DeprecationWarning.warn("createInstanceByName", "createInstanceByNameAsync")
97101
if (viewModelName == null) throw UnsupportedOperationException(NO_NAME_ERROR)
98102
return try {
99103
runBlocking { createInstanceByNameImpl(name) }
100104
} catch (e: UnsupportedOperationException) {
101105
throw e
102106
} catch (e: Exception) {
103-
Log.e(TAG, "createInstanceByName('$name') failed", e)
107+
RiveLog.e(TAG, "createInstanceByName('$name') failed: ${e.message}")
104108
null
105109
}
106110
}
@@ -112,12 +116,13 @@ class HybridViewModel(
112116

113117
// Deprecated: Use createDefaultInstanceAsync instead
114118
override fun createDefaultInstance(): HybridViewModelInstanceSpec? {
119+
DeprecationWarning.warn("createDefaultInstance", "createDefaultInstanceAsync")
115120
return try {
116121
val source = vmSource.defaultInstance()
117122
val vmi = ViewModelInstance.fromFile(riveFile, source)
118123
HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName)
119124
} catch (e: Exception) {
120-
Log.e(TAG, "createDefaultInstance failed", e)
125+
RiveLog.e(TAG, "createDefaultInstance failed: ${e.message}")
121126
null
122127
}
123128
}
@@ -132,12 +137,13 @@ class HybridViewModel(
132137

133138
// Deprecated: Use createBlankInstanceAsync instead
134139
override fun createInstance(): HybridViewModelInstanceSpec? {
140+
DeprecationWarning.warn("createInstance", "createBlankInstanceAsync")
135141
return try {
136142
val source = vmSource.blankInstance()
137143
val vmi = ViewModelInstance.fromFile(riveFile, source)
138144
HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName)
139145
} catch (e: Exception) {
140-
Log.e(TAG, "createInstance (blank) failed", e)
146+
RiveLog.e(TAG, "createInstance (blank) failed: ${e.message}")
141147
null
142148
}
143149
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModelBooleanProperty.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ class HybridViewModelBooleanProperty(
2222
// Deprecated: Use getValueAsync (read) or set(value) (write) instead
2323
override var value: Boolean
2424
get() {
25+
DeprecationWarning.warn("BooleanProperty.value", "getValueAsync")
2526
return try {
2627
runBlocking { instance.getBooleanFlow(path).first() }
2728
} catch (e: Exception) {
28-
Log.e(TAG, "getValue failed for path '$path'", e)
29+
RiveLog.e(TAG, "getValue failed for path '$path': ${e.message}")
2930
false
3031
}
3132
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModelColorProperty.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ class HybridViewModelColorProperty(
2222
// Deprecated: Use getValueAsync (read) or set(value) (write) instead
2323
override var value: Double
2424
get() {
25+
DeprecationWarning.warn("ColorProperty.value", "getValueAsync")
2526
return try {
2627
runBlocking { instance.getColorFlow(path).first() }.toDouble()
2728
} catch (e: Exception) {
28-
Log.e(TAG, "getValue failed for path '$path'", e)
29+
RiveLog.e(TAG, "getValue failed for path '$path': ${e.message}")
2930
0.0
3031
}
3132
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModelEnumProperty.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ class HybridViewModelEnumProperty(
2222
// Deprecated: Use getValueAsync (read) or set(value) (write) instead
2323
override var value: String
2424
get() {
25+
DeprecationWarning.warn("EnumProperty.value", "getValueAsync")
2526
return try {
2627
runBlocking { instance.getEnumFlow(path).first() }
2728
} catch (e: Exception) {
28-
Log.e(TAG, "getValue failed for path '$path'", e)
29+
RiveLog.e(TAG, "getValue failed for path '$path': ${e.message}")
2930
""
3031
}
3132
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModelInstance.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,11 @@ class HybridViewModelInstance(
141141

142142
// Deprecated: Use viewModelAsync instead
143143
override fun viewModel(path: String): HybridViewModelInstanceSpec? {
144+
DeprecationWarning.warn("viewModel", "viewModelAsync")
144145
return try {
145146
viewModelImpl(path)
146147
} catch (e: Exception) {
147-
Log.e(TAG, "viewModel failed for path '$path'", e)
148+
RiveLog.e(TAG, "viewModel failed for path '$path': ${e.message}")
148149
null
149150
}
150151
}

android/src/new/java/com/margelo/nitro/rive/HybridViewModelListProperty.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ class HybridViewModelListProperty(
2525
// Deprecated: Use getLengthAsync instead
2626
override val length: Double
2727
get() {
28+
DeprecationWarning.warn("ListProperty.length", "getLengthAsync")
2829
return try {
2930
runBlocking { instance.getListSize(path) }.toDouble()
3031
} catch (e: Exception) {
31-
Log.e(TAG, "getListSize failed for path '$path'", e)
32+
RiveLog.e(TAG, "getListSize failed for path '$path': ${e.message}")
3233
0.0
3334
}
3435
}
@@ -46,10 +47,11 @@ class HybridViewModelListProperty(
4647

4748
// Deprecated: Use getInstanceAtAsync instead
4849
override fun getInstanceAt(index: Double): HybridViewModelInstanceSpec? {
50+
DeprecationWarning.warn("ListProperty.getInstanceAt", "getInstanceAtAsync")
4951
return try {
5052
runBlocking { fetchInstanceAt(index) }
5153
} catch (e: Exception) {
52-
Log.e(TAG, "getInstanceAt($index) failed for path '$path'", e)
54+
RiveLog.e(TAG, "getInstanceAt($index) failed for path '$path': ${e.message}")
5355
null
5456
}
5557
}

0 commit comments

Comments
 (0)