[android] remove legacy references to ResourceBuffer/RenderCallback and switch from JNA to JNI for getting native window handle
This commit is contained in:
@@ -43,22 +43,18 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 22
|
minSdkVersion 22
|
||||||
ndk {
|
|
||||||
abiFilters 'arm64-v8a' // 'x86_64' 'armeabi-v7a'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path "CMakeLists.txt"
|
path "CMakeLists.txt"
|
||||||
version "3.18.1"
|
version "3.22.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
api "net.java.dev.jna:jna:5.10.0@aar"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,16 @@
|
|||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include <android/native_activity.h>
|
#include <android/native_activity.h>
|
||||||
|
#include <jni.h>
|
||||||
#include "ResourceBuffer.h"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
JNIEXPORT jlong JNICALL Java_dev_thermion_android_NativeWindowHelper_00024Companion_getNativeWindowFromSurface(
|
||||||
typedef void (*FilamentRenderCallback)(void *const owner);
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
void* get_native_window_from_surface(
|
jobject surface
|
||||||
jobject surface,
|
) {
|
||||||
JNIEnv* env
|
ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
|
||||||
) {
|
return (jlong)window;
|
||||||
void* window = ANativeWindow_fromSurface(env, surface);
|
}
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceLoaderWrapper* make_resource_loader_wrapper_android(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void* owner) {
|
|
||||||
ResourceLoaderWrapper *rlw = (ResourceLoaderWrapper *)malloc(sizeof(ResourceLoaderWrapper));
|
|
||||||
rlw->loadToOut = nullptr;
|
|
||||||
rlw->freeResource = nullptr;
|
|
||||||
rlw->loadResource = nullptr;
|
|
||||||
rlw->loadFromOwner = loadFn;
|
|
||||||
rlw->freeFromOwner = freeFn;
|
|
||||||
rlw->owner = owner;
|
|
||||||
return rlw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this does nothing, but we need it for JNA to return the correct pointer
|
|
||||||
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback callback) {
|
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
#ifndef RESOURCE_BUFFER_H
|
|
||||||
#define RESOURCE_BUFFER_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
//
|
|
||||||
// A ResourceBuffer is a unified interface for working with
|
|
||||||
// binary assets across various platforms.
|
|
||||||
// This is simply:
|
|
||||||
// 1) a pointer to some data
|
|
||||||
// 2) the length of the data
|
|
||||||
// 3) an ID that can be passed back to the native platform to release the underlying asset when needed.
|
|
||||||
//
|
|
||||||
typedef struct ResourceBuffer
|
|
||||||
{
|
|
||||||
const void *const data;
|
|
||||||
const int32_t size;
|
|
||||||
const int32_t id;
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
ResourceBuffer(void *const data, int32_t size, int32_t id) : data(data), size(size), id(id) {}
|
|
||||||
#endif
|
|
||||||
} ResourceBuffer;
|
|
||||||
|
|
||||||
typedef void (*LoadFilamentResourceIntoOutPointer)(const char *uri, ResourceBuffer *out);
|
|
||||||
typedef ResourceBuffer (*LoadFilamentResource)(const char *uri);
|
|
||||||
typedef ResourceBuffer (*LoadFilamentResourceFromOwner)(const char *const, void *const owner);
|
|
||||||
typedef void (*FreeFilamentResource)(ResourceBuffer);
|
|
||||||
typedef void (*FreeFilamentResourceFromOwner)(ResourceBuffer, void *const owner);
|
|
||||||
|
|
||||||
typedef struct ResourceLoaderWrapper
|
|
||||||
{
|
|
||||||
LoadFilamentResource loadResource;
|
|
||||||
FreeFilamentResource freeResource;
|
|
||||||
LoadFilamentResourceFromOwner loadFromOwner;
|
|
||||||
FreeFilamentResourceFromOwner freeFromOwner;
|
|
||||||
void *owner;
|
|
||||||
LoadFilamentResourceIntoOutPointer loadToOut;
|
|
||||||
} ResourceLoaderWrapper;
|
|
||||||
|
|
||||||
void *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package dev.thermion.android
|
|
||||||
|
|
||||||
import com.sun.jna.ptr.PointerByReference
|
|
||||||
import com.sun.jna.ptr.IntByReference
|
|
||||||
|
|
||||||
import android.view.Surface
|
|
||||||
|
|
||||||
import android.content.res.AssetManager
|
|
||||||
import com.sun.jna.*
|
|
||||||
|
|
||||||
open class ResourceBuffer: Structure(), Structure.ByValue {
|
|
||||||
@JvmField var data: Pointer = Pointer(0);
|
|
||||||
@JvmField var size: Int = 0;
|
|
||||||
@JvmField var id: Int = 0;
|
|
||||||
override fun getFieldOrder(): List<String> {
|
|
||||||
return listOf("data", "size", "id")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoadFilamentResourceFromOwner : Callback {
|
|
||||||
fun loadResourceFromOwner(resourceName: String?, owner: Pointer?): ResourceBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FreeFilamentResourceFromOwner : Callback {
|
|
||||||
fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RenderCallback : Callback {
|
|
||||||
fun renderCallback(owner:Pointer?)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FilamentInterop : Library {
|
|
||||||
|
|
||||||
fun get_native_window_from_surface(surface:Object, env:JNIEnv) : Pointer?;
|
|
||||||
fun make_render_callback_fn_pointer(renderCallback:RenderCallback) : Pointer
|
|
||||||
fun make_resource_loader_wrapper_android(loadResourceFromOwner: LoadFilamentResourceFromOwner, freeResource: FreeFilamentResourceFromOwner, owner:Pointer?) : Pointer;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import java.io.*
|
|
||||||
import java.nio.file.FileSystems
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.absolutePathString
|
|
||||||
import kotlin.io.path.listDirectoryEntries
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
|
|
||||||
class HotReloadPathHelper {
|
|
||||||
companion object {
|
|
||||||
fun getAssetPath(path: String, packageName: String): String? {
|
|
||||||
val packagePath = "/data/user/0/${packageName}/code_cache/"
|
|
||||||
Log.v("thermion_flutter", "Looking for hot reloaded asset ${path} under package path ${packagePath}")
|
|
||||||
val files = File(packagePath).walkBottomUp().filter {
|
|
||||||
it.path.endsWith(path)
|
|
||||||
}.sortedBy {
|
|
||||||
it.lastModified()
|
|
||||||
}.toList()
|
|
||||||
if(files.size > 0) {
|
|
||||||
Log.v("thermion_flutter", "Using hot reloaded asset at ${files.last().path}")
|
|
||||||
return files.last().path;
|
|
||||||
}
|
|
||||||
Log.v("thermion_flutter", "No hot reloaded asset found.")
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package dev.thermion.android
|
package dev.thermion.android
|
||||||
|
|
||||||
|
|
||||||
import HotReloadPathHelper
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
@@ -12,7 +10,6 @@ import android.view.Surface
|
|||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import com.sun.jna.*
|
|
||||||
import io.flutter.FlutterInjector
|
import io.flutter.FlutterInjector
|
||||||
import io.flutter.embedding.engine.FlutterJNI
|
import io.flutter.embedding.engine.FlutterJNI
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
@@ -26,34 +23,16 @@ import io.flutter.view.TextureRegistry.SurfaceTextureEntry
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
class NativeWindowHelper {
|
||||||
class LoadFilamentResourceFromOwnerImpl(plugin:ThermionFlutterPlugin) : LoadFilamentResourceFromOwner {
|
companion object {
|
||||||
var plugin = plugin
|
init {
|
||||||
override fun loadResourceFromOwner(path: String?, owner: Pointer?): ResourceBuffer {
|
System.loadLibrary("thermion_flutter_android")
|
||||||
return plugin.loadResourceFromOwner(path, owner)
|
}
|
||||||
}
|
external fun getNativeWindowFromSurface(surface: Surface): Long
|
||||||
}
|
|
||||||
|
|
||||||
class FreeFilamentResourceFromOwnerImpl(plugin:ThermionFlutterPlugin) : FreeFilamentResourceFromOwner {
|
|
||||||
var plugin = plugin
|
|
||||||
override fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?) {
|
|
||||||
plugin.freeResourceFromOwner(rb, owner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RenderCallbackImpl(plugin:ThermionFlutterPlugin) : RenderCallback {
|
|
||||||
var plugin = plugin
|
|
||||||
override fun renderCallback(owner:Pointer?) {
|
|
||||||
plugin.renderCallback();
|
|
||||||
|
|
||||||
if(!plugin._surface!!.isValid) {
|
|
||||||
Log.e("thermion_flutter", "Error: surface is no longer valid")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ThermionFlutterPlugin */
|
class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||||
class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, LoadFilamentResourceFromOwner, FreeFilamentResourceFromOwner {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_NAME = "dev.thermion.flutter/event"
|
const val CHANNEL_NAME = "dev.thermion.flutter/event"
|
||||||
@@ -66,8 +45,6 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo
|
|||||||
|
|
||||||
private var lifecycle: Lifecycle? = null
|
private var lifecycle: Lifecycle? = null
|
||||||
|
|
||||||
private lateinit var _lib : FilamentInterop
|
|
||||||
|
|
||||||
private data class TextureEntry(
|
private data class TextureEntry(
|
||||||
val surfaceTextureEntry: SurfaceTextureEntry,
|
val surfaceTextureEntry: SurfaceTextureEntry,
|
||||||
val surfaceTexture: SurfaceTexture,
|
val surfaceTexture: SurfaceTexture,
|
||||||
@@ -79,17 +56,12 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo
|
|||||||
var _surface: Surface? = null
|
var _surface: Surface? = null
|
||||||
private val textures: MutableMap<Long, TextureEntry> = mutableMapOf()
|
private val textures: MutableMap<Long, TextureEntry> = mutableMapOf()
|
||||||
|
|
||||||
|
|
||||||
private lateinit var activity:Activity
|
private lateinit var activity:Activity
|
||||||
|
|
||||||
private var loadResourceWrapper:LoadFilamentResourceFromOwnerImpl = LoadFilamentResourceFromOwnerImpl(this)
|
|
||||||
private var freeResourceWrapper:FreeFilamentResourceFromOwnerImpl = FreeFilamentResourceFromOwnerImpl(this)
|
|
||||||
|
|
||||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
this.flutterPluginBinding = flutterPluginBinding
|
this.flutterPluginBinding = flutterPluginBinding
|
||||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME)
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME)
|
||||||
channel.setMethodCallHandler(this)
|
channel.setMethodCallHandler(this)
|
||||||
_lib = Native.loadLibrary("thermion_flutter_android", FilamentInterop::class.java, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||||
@@ -98,68 +70,6 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo
|
|||||||
activity.window.setFormat(PixelFormat.RGBA_8888)
|
activity.window.setFormat(PixelFormat.RGBA_8888)
|
||||||
}
|
}
|
||||||
|
|
||||||
val _resources:MutableMap<ResourceBuffer,Memory> = mutableMapOf();
|
|
||||||
var _lastId = 1
|
|
||||||
|
|
||||||
override fun loadResourceFromOwner(path: String?, owner: Pointer?): ResourceBuffer {
|
|
||||||
Log.i("thermion_flutter", "Loading resource from path $path")
|
|
||||||
var data:ByteArray? = null
|
|
||||||
if(path!!.startsWith("file://")) {
|
|
||||||
data = File(path!!.substring(6)).readBytes()
|
|
||||||
} else {
|
|
||||||
var assetPath = path
|
|
||||||
if(assetPath.startsWith("asset://")) {
|
|
||||||
assetPath = assetPath!!.substring(8)
|
|
||||||
}
|
|
||||||
val loader = FlutterInjector.instance().flutterLoader()
|
|
||||||
val key = loader.getLookupKeyForAsset(assetPath)
|
|
||||||
val hotReloadPath = HotReloadPathHelper.getAssetPath(key, activity.getPackageName())
|
|
||||||
if (hotReloadPath != null) {
|
|
||||||
data = File(hotReloadPath).readBytes()
|
|
||||||
} else {
|
|
||||||
Log.i("thermion_flutter", "Loading resource from main asset bundle at ${assetPath}")
|
|
||||||
|
|
||||||
val assetManager: AssetManager = activity.assets
|
|
||||||
try {
|
|
||||||
data = assetManager.open(key).readBytes()
|
|
||||||
Log.i("thermion_flutter", "Loaded ${data.size} bytes")
|
|
||||||
} catch (e:Exception) {
|
|
||||||
Log.e("thermion_flutter", "Failed to open asset at ${assetPath}", null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val rb = ResourceBuffer();
|
|
||||||
try {
|
|
||||||
if (data != null) {
|
|
||||||
val dataPtr = Memory(data.size.toLong())
|
|
||||||
dataPtr.write(0, data, 0, data.size)
|
|
||||||
rb.data = dataPtr
|
|
||||||
rb.size = data.size
|
|
||||||
rb.id = _lastId
|
|
||||||
_resources[rb] = dataPtr;
|
|
||||||
_lastId++
|
|
||||||
} else {
|
|
||||||
rb.id = 0
|
|
||||||
rb.size = 0
|
|
||||||
rb.data = Pointer(0)
|
|
||||||
}
|
|
||||||
} catch(e:Exception) {
|
|
||||||
Log.e("thermion_flutter", "Error setting resource buffer : $e", null);
|
|
||||||
}
|
|
||||||
rb.write();
|
|
||||||
return rb;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?) {
|
|
||||||
_resources.remove(rb)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renderCallback() {
|
|
||||||
// noop, log or check surface.valid() is you want
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
@@ -184,8 +94,10 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo
|
|||||||
} else {
|
} else {
|
||||||
val flutterTextureId = surfaceTextureEntry.id()
|
val flutterTextureId = surfaceTextureEntry.id()
|
||||||
textures[flutterTextureId] = TextureEntry(surfaceTextureEntry, surfaceTexture, surface)
|
textures[flutterTextureId] = TextureEntry(surfaceTextureEntry, surfaceTexture, surface)
|
||||||
val nativeWindow = _lib.get_native_window_from_surface(surface as Object, JNIEnv.CURRENT)
|
//val surface = surfaceView.holder.surface
|
||||||
result.success(listOf(flutterTextureId, flutterTextureId, Pointer.nativeValue(nativeWindow)))
|
val nativeWindowPtr = NativeWindowHelper.getNativeWindowFromSurface(surface)
|
||||||
|
//val nativeWindow = _lib.get_native_window_from_surface(surface as Object, JNIEnv.CURRENT)
|
||||||
|
result.success(listOf(flutterTextureId, flutterTextureId, nativeWindowPtr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"destroyTexture" -> {
|
"destroyTexture" -> {
|
||||||
@@ -215,10 +127,6 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo
|
|||||||
"getSharedContext" -> {
|
"getSharedContext" -> {
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
"getRenderCallback" -> {
|
|
||||||
val renderCallbackFnPointer = _lib.make_render_callback_fn_pointer(RenderCallbackImpl(this))
|
|
||||||
result.success(listOf(Pointer.nativeValue(renderCallbackFnPointer), 0))
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
result.notImplemented()
|
result.notImplemented()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user