diff --git a/.rive_head b/.rive_head index 65ac11c0a..194f6df0d 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -57d81702cb268580c8fbbb23084aa8a069982a0e +df6ccaa5ff68ede57ae658e8b2a3af4aedeec1a8 diff --git a/kotlin/src/main/java/app/rive/runtime/kotlin/RiveInitializer.kt b/kotlin/src/main/java/app/rive/runtime/kotlin/RiveInitializer.kt index d118933ab..daf4664ea 100644 --- a/kotlin/src/main/java/app/rive/runtime/kotlin/RiveInitializer.kt +++ b/kotlin/src/main/java/app/rive/runtime/kotlin/RiveInitializer.kt @@ -3,6 +3,7 @@ package app.rive.runtime.kotlin import android.content.Context import androidx.startup.Initializer import app.rive.runtime.kotlin.core.Rive +import app.rive.runtime.kotlin.core.RiveInitialized /** * Initializes Rive; needs to be done at startup. @@ -37,9 +38,9 @@ import app.rive.runtime.kotlin.core.Rive * In fact, if you want to provide a custom renderer type you'll need to init Rive manually. */ -class RiveInitializer : Initializer { - override fun create(context: Context) { - return Rive.init(context) +class RiveInitializer : Initializer { + override fun create(context: Context): RiveInitialized { + return RiveInitialized(Rive.init(context)) } override fun dependencies(): List>> { diff --git a/kotlin/src/main/java/app/rive/runtime/kotlin/core/NativeLoader.kt b/kotlin/src/main/java/app/rive/runtime/kotlin/core/NativeLoader.kt new file mode 100644 index 000000000..6885760e3 --- /dev/null +++ b/kotlin/src/main/java/app/rive/runtime/kotlin/core/NativeLoader.kt @@ -0,0 +1,62 @@ +package app.rive.runtime.kotlin.core + +import android.content.Context +import android.os.Build +import com.getkeepsafe.relinker.ReLinker + +internal object NativeLoader { + + /** + * Loads specified library by name and covers edge cases. + * ReLinker is only needed for API Level < 23. + * + * @param context required for ReLinker + * @param name the name of the library to load + * @return `true` if successfully loaded + */ + fun loadLibrary(context: Context, name: String): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return loadLibrary(name) + } + + val relinkerLoaded = runCatching { + ReLinker.loadLibrary(context, name) + }.isSuccess + + // Ignore ReLinker result first, since it catches some exception and may not be loaded + // resulting in false-positives + // Loading twice won't work anyway so there is no problem + return loadLibrary(name) || relinkerLoaded + } + + /** + * Loads specified library by name and covers edge cases. + * May fail on some API Level < 23 devices. + * + * @param name the name of the library to load + * @return `true` if successfully loaded + */ + private fun loadLibrary(name: String): Boolean { + var loaded = true + + // normally it's just the name but some manufacturers mess with this + systemLoadLibrary(name) { + systemLoadLibrary("$name.so") { + systemLoadLibrary("lib$name") { + systemLoadLibrary("lib$name.so") { + loaded = false + } + } + } + } + return loaded + } + + private fun systemLoadLibrary(name: String, onError: () -> Unit = { }) { + if (runCatching { + System.loadLibrary(name) + }.isFailure) { + onError() + } + } +} \ No newline at end of file diff --git a/kotlin/src/main/java/app/rive/runtime/kotlin/core/Rive.kt b/kotlin/src/main/java/app/rive/runtime/kotlin/core/Rive.kt index a515e4204..d8f68addc 100644 --- a/kotlin/src/main/java/app/rive/runtime/kotlin/core/Rive.kt +++ b/kotlin/src/main/java/app/rive/runtime/kotlin/core/Rive.kt @@ -40,12 +40,13 @@ object Rive { * [app.rive.runtime.kotlin.RiveAnimationView][RiveAnimationView]. Defaults to * [RendererType.Rive]. */ - fun init(context: Context, defaultRenderer: RendererType = RendererType.Rive) { - // NOTE: loadLibrary also allows us to specify a version, something we might want to take - // advantage of - ReLinker.loadLibrary(context, RIVE_ANDROID) + fun init(context: Context, defaultRenderer: RendererType = RendererType.Rive): Boolean { + val loaded = NativeLoader.loadLibrary(context, RIVE_ANDROID) defaultRendererType = defaultRenderer - initializeCppEnvironment() + if (loaded) { + initializeCppEnvironment() + } + return loaded } /** diff --git a/kotlin/src/main/java/app/rive/runtime/kotlin/core/RiveInitialized.kt b/kotlin/src/main/java/app/rive/runtime/kotlin/core/RiveInitialized.kt new file mode 100644 index 000000000..bdbd8bc5f --- /dev/null +++ b/kotlin/src/main/java/app/rive/runtime/kotlin/core/RiveInitialized.kt @@ -0,0 +1,13 @@ +package app.rive.runtime.kotlin.core + +import app.rive.runtime.kotlin.RiveInitializer + +/** + * Represents the result of the [Rive] initialization. + * Only used for [RiveInitializer] to be compatible with other initializers. + * + * @property success Indicates whether the initialization was successful. + */ +data class RiveInitialized( + val success: Boolean +) diff --git a/submodules/rive-runtime b/submodules/rive-runtime index 695dfdaff..1d9f9f831 160000 --- a/submodules/rive-runtime +++ b/submodules/rive-runtime @@ -1 +1 @@ -Subproject commit 695dfdaffc3d63172a81567d698c4e59d1bae6b4 +Subproject commit 1d9f9f83124fec40403a13ca2310dba25f9d1aeb