diff --git a/android/src/main/cpp/sonar.cpp b/android/src/main/cpp/sonar.cpp index 22d7f403c..9161d30df 100644 --- a/android/src/main/cpp/sonar.cpp +++ b/android/src/main/cpp/sonar.cpp @@ -22,6 +22,7 @@ #include #include #include +#include using namespace facebook; using namespace facebook::sonar; @@ -198,6 +199,37 @@ class JSonarPlugin : public jni::JavaClass { } }; +class JSonarStateUpdateListener : public jni::JavaClass { + public: + constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarStateUpdateListener;"; + + void onUpdate() { + static const auto method = javaClassStatic()->getMethod("onUpdate"); + method(self()); + } + void onStepStarted(std::string step) { + static const auto method = javaClassStatic()->getMethod("onStepStarted"); + method(self(), step); + } + void onStepSuccess(std::string step) { + static const auto method = javaClassStatic()->getMethod("onStepSuccess"); + method(self(), step); + } + void onStepFailed(std::string step, std::string errorMessage) { + static const auto method = javaClassStatic()->getMethod("onStepFailed"); + method(self(), step, errorMessage); + } +}; + +class AndroidSonarStateUpdateListener : public SonarStateUpdateListener { + public: + AndroidSonarStateUpdateListener(jni::alias_ref stateListener); + void onUpdate(); + + private: + jni::global_ref jStateListener; +}; + class JSonarPluginWrapper : public SonarPlugin { public: jni::global_ref jplugin; @@ -229,7 +261,10 @@ class JSonarClient : public jni::HybridClass { makeNativeMethod("stop", JSonarClient::stop), makeNativeMethod("addPlugin", JSonarClient::addPlugin), makeNativeMethod("removePlugin", JSonarClient::removePlugin), + makeNativeMethod("subscribeForUpdates", JSonarClient::subscribeForUpdates), + makeNativeMethod("unsubscribe", JSonarClient::unsubscribe), makeNativeMethod("getPlugin", JSonarClient::getPlugin), + makeNativeMethod("getState", JSonarClient::getState), }); } @@ -256,6 +291,22 @@ class JSonarClient : public jni::HybridClass { client->removePlugin(client->getPlugin(plugin->identifier())); } + void subscribeForUpdates(jni::alias_ref stateListener) { + auto client = SonarClient::instance(); + mStateListener = std::make_shared(stateListener); + client->setStateListener(mStateListener); + } + + void unsubscribe() { + auto client = SonarClient::instance(); + mStateListener = nullptr; + client->setStateListener(nullptr); + } + + std::string getState() { + return SonarClient::instance()->getState(); + } + jni::alias_ref getPlugin(const std::string& identifier) { auto plugin = SonarClient::instance()->getPlugin(identifier); if (plugin) { @@ -295,6 +346,7 @@ class JSonarClient : public jni::HybridClass { private: friend HybridBase; + std::shared_ptr mStateListener = nullptr; JSonarClient() {} }; @@ -309,3 +361,11 @@ jint JNI_OnLoad(JavaVM* vm, void*) { JEventBase::registerNatives(); }); } + +AndroidSonarStateUpdateListener::AndroidSonarStateUpdateListener(jni::alias_ref stateListener) { + jStateListener = jni::make_global(stateListener); +} + +void AndroidSonarStateUpdateListener::onUpdate() { + jStateListener->onUpdate(); +} diff --git a/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java b/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java index 72c250d62..3d55e2108 100644 --- a/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java +++ b/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java @@ -13,6 +13,7 @@ import com.facebook.soloader.SoLoader; import com.facebook.sonar.BuildConfig; import com.facebook.sonar.core.SonarClient; import com.facebook.sonar.core.SonarPlugin; +import com.facebook.sonar.core.SonarStateUpdateListener; @DoNotStrip class SonarClientImpl implements SonarClient { @@ -55,4 +56,13 @@ class SonarClientImpl implements SonarClient { @Override public native void stop(); + + @Override + public native void subscribeForUpdates(SonarStateUpdateListener stateListener); + + @Override + public native void unsubscribe(); + + @Override + public native String getState(); } diff --git a/android/src/main/java/com/facebook/sonar/core/SonarClient.java b/android/src/main/java/com/facebook/sonar/core/SonarClient.java index 8c91c3599..2cbf33ece 100644 --- a/android/src/main/java/com/facebook/sonar/core/SonarClient.java +++ b/android/src/main/java/com/facebook/sonar/core/SonarClient.java @@ -17,4 +17,10 @@ public interface SonarClient { void start(); void stop(); + + void subscribeForUpdates(SonarStateUpdateListener stateListener); + + void unsubscribe(); + + String getState(); } diff --git a/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java b/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java new file mode 100644 index 000000000..c790fdb6f --- /dev/null +++ b/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java @@ -0,0 +1,7 @@ +package com.facebook.sonar.core; + +public interface SonarStateUpdateListener { + + void onUpdate(); + +} diff --git a/xplat/Sonar/SonarClient.cpp b/xplat/Sonar/SonarClient.cpp index a7d88ee09..2040f9ab9 100644 --- a/xplat/Sonar/SonarClient.cpp +++ b/xplat/Sonar/SonarClient.cpp @@ -9,6 +9,8 @@ #include "SonarClient.h" #include "SonarConnectionImpl.h" #include "SonarResponderImpl.h" +#include "SonarState.h" +#include "SonarStep.h" #include "SonarWebSocketImpl.h" #ifdef __ANDROID__ @@ -37,6 +39,12 @@ SonarClient* SonarClient::instance() { return kInstance; } +void SonarClient::setStateListener( + std::shared_ptr stateListener) { + SONAR_LOG("Setting state listener"); + sonarState_->setUpdateListener(stateListener); +} + void SonarClient::addPlugin(std::shared_ptr plugin) { SONAR_LOG(("SonarClient::addPlugin " + plugin->identifier()).c_str()); @@ -198,6 +206,10 @@ void SonarClient::performAndReportError(const std::function& func) { } } +std::string SonarClient::getState() { + return sonarState_->getState(); +} + } // namespace sonar } // namespace facebook diff --git a/xplat/Sonar/SonarClient.h b/xplat/Sonar/SonarClient.h index 6297d8bb5..d569ab3ff 100644 --- a/xplat/Sonar/SonarClient.h +++ b/xplat/Sonar/SonarClient.h @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include "SonarStep.h" namespace facebook { namespace sonar { @@ -39,6 +41,7 @@ class SonarClient : public SonarWebSocket::Callbacks { */ SonarClient(std::unique_ptr socket) : socket_(std::move(socket)) { + sonarState_ = std::make_unique(); socket_->setCallbacks(this); } @@ -62,8 +65,13 @@ class SonarClient : public SonarWebSocket::Callbacks { void refreshPlugins(); + void setStateListener( + std::shared_ptr stateListener); + std::shared_ptr getPlugin(const std::string& identifier); + std::string getState(); + template std::shared_ptr

getPlugin(const std::string& identifier) { return std::static_pointer_cast

(getPlugin(identifier)); @@ -78,6 +86,7 @@ class SonarClient : public SonarWebSocket::Callbacks { std::map> plugins_; std::map> connections_; std::mutex mutex_; + std::unique_ptr sonarState_; void performAndReportError(const std::function& func); void disconnect(std::shared_ptr plugin); diff --git a/xplat/Sonar/SonarState.cpp b/xplat/Sonar/SonarState.cpp new file mode 100644 index 000000000..0660e1b36 --- /dev/null +++ b/xplat/Sonar/SonarState.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ +#include "SonarState.h" +#include "SonarStateUpdateListener.h" +#include "SonarStep.h" + +/* Class responsible for collecting state updates and combining them into a + * view of the current state of the sonar client. */ + +SonarState::SonarState() { + stateUpdates = ""; +} +void SonarState::setUpdateListener( + std::shared_ptr listener) { + mListener = listener; +} + +void SonarState::started(std::string step) { + stateUpdates = stateUpdates + "[Started] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +void SonarState::success(std::string step) { + stateUpdates = stateUpdates + "[Success] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +void SonarState::failed(std::string step, std::string errorMessage) { + stateUpdates = stateUpdates + "[Failed] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +// TODO: Currently returns string, but should really provide a better +// representation of the current state so the UI can show it in a more intuitive +// way +std::string SonarState::getState() { + return stateUpdates; +} + +std::shared_ptr SonarState::start(std::string step_name) { + started(step_name); + return std::make_shared(step_name, this); +} diff --git a/xplat/Sonar/SonarState.h b/xplat/Sonar/SonarState.h new file mode 100644 index 000000000..8d09d08fb --- /dev/null +++ b/xplat/Sonar/SonarState.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +#pragma once + +#include +#include + +class SonarStep; +class SonarStateUpdateListener; + +class SonarState { + friend SonarStep; + + public: + SonarState(); + void setUpdateListener(std::shared_ptr); + std::string getState(); + + /* To record a state update, call start() with the name of the step to get a + SonarStep object. Call complete on this to register it successful, + the absense of the completion call when it is destructed will register as a + step failure. */ + std::shared_ptr start(std::string step); + + private: + void success(std::string); + void failed(std::string, std::string); + void started(std::string); + std::shared_ptr mListener = nullptr; + std::string stateUpdates; +}; diff --git a/xplat/Sonar/SonarStateUpdateListener.h b/xplat/Sonar/SonarStateUpdateListener.h new file mode 100644 index 000000000..a69143b1f --- /dev/null +++ b/xplat/Sonar/SonarStateUpdateListener.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +#pragma once + +class SonarStateUpdateListener { + public: + virtual void onUpdate() = 0; +}; diff --git a/xplat/Sonar/SonarStep.cpp b/xplat/Sonar/SonarStep.cpp new file mode 100644 index 000000000..74475394f --- /dev/null +++ b/xplat/Sonar/SonarStep.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ +#include "SonarStep.h" +#include "SonarState.h" + +void SonarStep::complete() { + isCompleted = true; + state->success(name); +} + +SonarStep::SonarStep(std::string step, SonarState* s) { + state = s; + name = step; +} + +SonarStep::~SonarStep() { + if (!isCompleted) { + state->failed(name, ""); + } +} diff --git a/xplat/Sonar/SonarStep.h b/xplat/Sonar/SonarStep.h new file mode 100644 index 000000000..37bfef11a --- /dev/null +++ b/xplat/Sonar/SonarStep.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +#pragma once + +#include + +class SonarState; + +class SonarStep { + public: + void complete(); + SonarStep(std::string name, SonarState* state); + ~SonarStep(); + + private: + std::string name; + bool isCompleted = false; + SonarState* state; +};