Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions rnmodules/react-native-kb/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ project(cpp)
cmake_minimum_required(VERSION 3.4.1)

set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD 20)
set (NODE_MODULES_DIR "${CMAKE_SOURCE_DIR}/../..")

add_library(cpp
Expand All @@ -16,12 +16,12 @@ message(INFO "params: ${NODE_MODULES_DIR}")
# Specifies a path to native header files.
include_directories(
../cpp
"${NODE_MODULES_DIR}/msgpack-cxx-6.1.0/include"
"${NODE_MODULES_DIR}/msgpack-cxx-7.0.0/include"
)

set_target_properties(
cpp PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON
)
Expand Down
201 changes: 62 additions & 139 deletions rnmodules/react-native-kb/android/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,158 +1,81 @@
// https://github.com/ammarahm-ed/react-native-jsi-template/blob/master/android/cpp-adapter.cpp
#include "pthread.h"
#include "react-native-kb.h"
#include <ReactCommon/BindingsInstallerHolder.h>
#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/CallInvokerHolder.h>
#include <android/log.h>
#include <fbjni/fbjni.h>
#include <jni.h>
#include <jsi/jsi.h>
#include <sys/types.h>

using namespace facebook;
using namespace facebook::jsi;
using namespace std;
using namespace kb;
using namespace facebook::react;

JavaVM *java_vm = NULL;
jclass java_class;
jobject java_object;

/**
* A simple callback function that allows us to detach current JNI Environment
* when the thread
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/

void DeferThreadDetach(JNIEnv *env) {
static pthread_key_t thread_key;

// Set up a Thread Specific Data key, and a callback that
// will be executed when a thread is destroyed.
// This is only done once, across all threads, and the value
// associated with the key for any given thread will initially
// be NULL.
static auto run_once = [] {
const auto err = pthread_key_create(&thread_key, [](void *ts_env) {
if (ts_env) {
java_vm->DetachCurrentThread();
}
});
if (err) {
// Failed to create TSD key. Throw an exception if you want to.
}
return 0;
}();
static_cast<void>(run_once);

// For the callback to actually be executed when a thread exits
// we need to associate a non-NULL value with the key on that thread.
// We can use the JNIEnv* as that value.
const auto ts_env = pthread_getspecific(thread_key);
if (!ts_env) {
if (pthread_setspecific(thread_key, env)) {
// Failed to set thread-specific value for key. Throw an exception if you
// want to.
}
}
}

/**
* Get a JNIEnv* valid for this thread, regardless of whether
* we're on a native thread or a Java thread.
* If the calling thread is not currently attached to the JVM
* it will be attached, and then automatically detached when the
* thread is destroyed.
*
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/
JNIEnv *GetJniEnv() {
JNIEnv *env = nullptr;
// We still call GetEnv first to detect if the thread already
// is attached. This is done to avoid setting up a DetachCurrentThread
// call on a Java thread.
struct JKbModule : jni::JavaClass<JKbModule> {
static constexpr auto kJavaDescriptor = "Lcom/reactnativekb/KbModule;";
};

// g_vm is a global.
auto get_env_result = java_vm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED) {
if (java_vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
DeferThreadDetach(env);
} else {
// Failed to attach thread. Throw an exception if you want to.
}
} else if (get_env_result == JNI_EVERSION) {
// Unsupported JNI version. Throw an exception if you want to.
class KbNativeAdapter {
public:
jni::global_ref<JKbModule::javaobject> jModule_;
std::shared_ptr<kb::KBBridge> bridge_;

explicit KbNativeAdapter(jni::alias_ref<JKbModule::javaobject> jModule)
: jModule_(jni::make_global(jModule)) {}

void writeToGo(void *ptr, size_t size) {
jni::ThreadScope scope;
auto env = jni::Environment::current();
auto jba = env->NewByteArray(size);
env->SetByteArrayRegion(jba, 0, size, (jbyte *)ptr);
static auto method =
JKbModule::javaClassStatic()
->getMethod<void(jni::alias_ref<jni::JArrayByte>)>("rpcOnGo");
method(jModule_, jni::wrap_alias(jba));
env->DeleteLocalRef(jba);
}
return env;
}

static jstring string2jstring(JNIEnv *env, const string &str) {
return (*env).NewStringUTF(str.c_str());
}
};

void install(facebook::jsi::Runtime &jsiRuntime) {
auto rpcOnGo = Function::createFromHostFunction(
jsiRuntime, PropNameID::forAscii(jsiRuntime, "rpcOnGo"), 1,
[](Runtime &runtime, const Value &thisValue, const Value *arguments,
size_t count) -> Value {
return RpcOnGo(
runtime, thisValue, arguments, count, [](void *ptr, size_t size) {
JNIEnv *jniEnv = GetJniEnv();
java_class = jniEnv->GetObjectClass(java_object);
jmethodID rpcOnGo =
jniEnv->GetMethodID(java_class, "rpcOnGo", "([B)V");
jbyteArray jba = jniEnv->NewByteArray(size);
jniEnv->SetByteArrayRegion(jba, 0, size, (jbyte *)ptr);
jvalue params[1];
params[0].l = jba;
jniEnv->CallVoidMethodA(java_object, rpcOnGo, params);
static std::shared_ptr<KbNativeAdapter> g_adapter;

static jni::local_ref<BindingsInstallerHolder::javaobject>
getBindingsInstaller(jni::alias_ref<JKbModule::javaobject> thiz) {
g_adapter = std::make_shared<KbNativeAdapter>(thiz);

return BindingsInstallerHolder::newObjectCxxArgs(
[adapter = g_adapter](jsi::Runtime &runtime,
const std::shared_ptr<CallInvoker> &callInvoker) {
if (adapter->bridge_) {
adapter->bridge_->teardown();
}
adapter->bridge_ = std::make_shared<kb::KBBridge>();
adapter->bridge_->install(
runtime, callInvoker,
[weak = std::weak_ptr(adapter)](void *ptr, size_t size) {
if (auto a = weak.lock())
a->writeToGo(ptr, size);
},
[](const std::string &err) {
__android_log_print(ANDROID_LOG_ERROR, "KBBridge",
"JSI error: %s", err.c_str());
});
});
jsiRuntime.global().setProperty(jsiRuntime, "rpcOnGo", std::move(rpcOnGo));
}

extern "C" JNIEXPORT void JNICALL installJSI(JNIEnv *env, jobject thiz, jlong jsi) {
auto runtime = reinterpret_cast<facebook::jsi::Runtime *>(jsi);
if (runtime) {
install(*runtime);
}
env->GetJavaVM(&java_vm);
java_object = env->NewGlobalRef(thiz);
static void nativeOnDataFromGo(jni::alias_ref<JKbModule::javaobject> thiz,
jni::alias_ref<jni::JArrayByte> data) {
auto adapter = g_adapter;
if (!adapter || !adapter->bridge_ || !data)
return;
auto pinned = data->pin();
adapter->bridge_->onDataFromGo(reinterpret_cast<uint8_t *>(pinned.get()),
pinned.size());
}

extern "C" JNIEXPORT void JNICALL emit(JNIEnv *env, jclass clazz, jlong jsi, jobject boxedCallInvokerHolder, jbyteArray data) {
auto rPtr = reinterpret_cast<facebook::jsi::Runtime *>(jsi);
auto &runtime = *rPtr;
auto boxedCallInvokerRef = jni::make_local(boxedCallInvokerHolder);
auto callInvokerHolder =
jni::dynamic_ref_cast<react::CallInvokerHolder::javaobject>(
boxedCallInvokerRef);
auto callInvoker = callInvokerHolder->cthis()->getCallInvoker();

auto size = static_cast<int>(env->GetArrayLength(data));
auto payloadBytes =
reinterpret_cast<uint8_t *>(env->GetByteArrayElements(data, nullptr));
auto values = PrepRpcOnJS(runtime, payloadBytes, size);
callInvoker->invokeAsync([values, &runtime]() {
RpcOnJS(runtime, values, [](const std::string &err) {
JNIEnv *jniEnv = GetJniEnv();
java_class = jniEnv->GetObjectClass(java_object);
jmethodID log =
jniEnv->GetMethodID(java_class, "log", "(Ljava/lang/String;)V");
auto s = string2jstring(jniEnv, err);
jvalue params[1];
params[0].l = s;
jniEnv->CallVoidMethodA(java_object, log, params);
});
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return jni::initialize(vm, [] {
jni::findClassStatic("com/reactnativekb/KbModule")
->registerNatives({
makeNativeMethod("getBindingsInstaller", getBindingsInstaller),
makeNativeMethod("nativeOnDataFromGo", nativeOnDataFromGo),
});
});
}

static JNINativeMethod methods[] = {
{"installJSI", "(J)V", (void *)&installJSI},
{"emit", "(JLcom/facebook/react/turbomodule/core/CallInvokerHolderImpl;[B)V", (void *)&emit},
};


extern "C" JNIEXPORT void JNICALL Java_com_reactnativekb_KbModule_registerNatives(JNIEnv *env, jobject thiz, jlong jsi) {
jclass clazz = env->FindClass("com/reactnativekb/KbModule");
env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(methods[0]));
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.reactnativekb

import kotlin.Throws

object DarkModePrefHelper {
fun fromString(prefString: String): DarkModePreference {
return when (prefString) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.reactnativekb

import kotlin.Throws

enum class DarkModePreference {
System,
AlwaysDark,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
package com.reactnativekb

import androidx.annotation.Nullable

import org.json.JSONException
import org.json.JSONObject

import java.io.File

class GuiConfig private constructor(filesDir: File?) {
private val filesDir: File?

init {
this.filesDir = filesDir
}

class GuiConfig private constructor(private val filesDir: File?) {
fun asString(): String? {
val filePath = File(filesDir, "/.config/keybase/gui_config.json")
return ReadFileAsString.read(filePath.getAbsolutePath())
return ReadFileAsString.read(filePath.absolutePath)
}

fun getDarkMode(): DarkModePreference {
return try {
val jsonObject = JSONObject(asString())
val jsonObject = JSONObject(asString() ?: return DarkModePreference.System)
val jsonObjectUI: JSONObject = jsonObject.getJSONObject("ui")
val darkModeString: String = jsonObjectUI.getString("darkMode")
DarkModePrefHelper.fromString(darkModeString)
Expand Down
Loading