| @@ -327,7 +327,7 @@ static const uint8 javaMidiByteCode[] = | |||||
| STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ | STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ | ||||
| STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;") | STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;") | ||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| @@ -113,4 +113,19 @@ Object withMember (Object copy, Member OtherObject::* member, Other&& value) | |||||
| template <typename Fn> struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; | template <typename Fn> struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; | ||||
| template <typename Fn> ScopeGuard (Fn) -> ScopeGuard<Fn>; | template <typename Fn> ScopeGuard (Fn) -> ScopeGuard<Fn>; | ||||
| #ifndef DOXYGEN | |||||
| namespace detail | |||||
| { | |||||
| template <typename Functor, typename Return, typename... Args> | |||||
| static constexpr auto toFnPtr (Functor functor, Return (Functor::*) (Args...) const) | |||||
| { | |||||
| return static_cast<Return (*) (Args...)> (functor); | |||||
| } | |||||
| } // namespace detail | |||||
| #endif | |||||
| /** Converts a captureless lambda to its equivalent function pointer type. */ | |||||
| template <typename Functor> | |||||
| static constexpr auto toFnPtr (Functor functor) { return detail::toFnPtr (functor, &Functor::operator()); } | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -62,7 +62,7 @@ static const uint8 invocationHandleByteCode[] = | |||||
| CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \ | CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \ | ||||
| CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V") | CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V") | ||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| @@ -116,7 +116,7 @@ struct SystemJavaClassComparator | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n) | |||||
| JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const uint8* bc, size_t n) | |||||
| : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr) | : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr) | ||||
| { | { | ||||
| SystemJavaClassComparator comparator; | SystemJavaClassComparator comparator; | ||||
| @@ -552,12 +552,12 @@ static const uint8 javaFragmentOverlay[] = | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (construct, "<init>", "()V") \ | METHOD (construct, "<init>", "()V") \ | ||||
| METHOD (close, "close", "()V") \ | METHOD (close, "close", "()V") \ | ||||
| CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \ | |||||
| CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \ | |||||
| CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \ | |||||
| CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V") | |||||
| CALLBACK (generatedCallback<&FragmentOverlay::onActivityResultCallback>, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \ | |||||
| CALLBACK (generatedCallback<&FragmentOverlay::onCreatedCallback>, "onCreateNative", "(JLandroid/os/Bundle;)V") \ | |||||
| CALLBACK (generatedCallback<&FragmentOverlay::onStartCallback>, "onStartNative", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&FragmentOverlay::onRequestPermissionsResultCallback>, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| @@ -590,47 +590,39 @@ void FragmentOverlay::open() | |||||
| env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get()); | env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get()); | ||||
| } | } | ||||
| void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host, | |||||
| jint requestCode, jint resultCode, jobject data) | |||||
| void FragmentOverlay::onCreatedCallback (JNIEnv* env, FragmentOverlay& t, jobject obj) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) | |||||
| myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data))); | |||||
| t.onCreated (LocalRef<jobject> { env->NewLocalRef (obj) }); | |||||
| } | } | ||||
| void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle) | |||||
| void FragmentOverlay::onStartCallback (JNIEnv*, FragmentOverlay& t) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) | |||||
| myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle))); | |||||
| t.onStart(); | |||||
| } | } | ||||
| void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host) | |||||
| void FragmentOverlay::onRequestPermissionsResultCallback (JNIEnv* env, FragmentOverlay& t, jint requestCode, jobjectArray jPermissions, jintArray jGrantResults) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) | |||||
| myself->onStart(); | |||||
| } | |||||
| Array<int> grantResults; | |||||
| int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0); | |||||
| void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode, | |||||
| jobjectArray jPermissions, jintArray jGrantResults) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) | |||||
| if (n > 0) | |||||
| { | { | ||||
| Array<int> grantResults; | |||||
| int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0); | |||||
| auto* data = env->GetIntArrayElements (jGrantResults, nullptr); | |||||
| if (n > 0) | |||||
| { | |||||
| auto* data = env->GetIntArrayElements (jGrantResults, nullptr); | |||||
| for (int i = 0; i < n; ++i) | |||||
| grantResults.add (data[i]); | |||||
| for (int i = 0; i < n; ++i) | |||||
| grantResults.add (data[i]); | |||||
| env->ReleaseIntArrayElements (jGrantResults, data, 0); | |||||
| } | |||||
| env->ReleaseIntArrayElements (jGrantResults, data, 0); | |||||
| } | |||||
| t.onRequestPermissionsResult (requestCode, | |||||
| javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)), | |||||
| grantResults); | |||||
| } | |||||
| myself->onRequestPermissionsResult (requestCode, | |||||
| javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)), | |||||
| grantResults); | |||||
| } | |||||
| void FragmentOverlay::onActivityResultCallback (JNIEnv* env, FragmentOverlay& t, jint requestCode, jint resultCode, jobject data) | |||||
| { | |||||
| t.onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data))); | |||||
| } | } | ||||
| jobject FragmentOverlay::getNativeHandle() | jobject FragmentOverlay::getNativeHandle() | ||||
| @@ -172,7 +172,7 @@ struct SystemJavaClassComparator; | |||||
| class JNIClassBase | class JNIClassBase | ||||
| { | { | ||||
| public: | public: | ||||
| JNIClassBase (const char* classPath, int minSDK, const void* byteCode, size_t byteCodeSize); | |||||
| JNIClassBase (const char* classPath, int minSDK, const uint8* byteCode, size_t byteCodeSize); | |||||
| virtual ~JNIClassBase(); | virtual ~JNIClassBase(); | ||||
| operator jclass() const noexcept { return classRef; } | operator jclass() const noexcept { return classRef; } | ||||
| @@ -210,6 +210,9 @@ private: | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| template <typename T, size_t N> constexpr auto numBytes (const T (&) [N]) { return N; } | |||||
| constexpr auto numBytes (std::nullptr_t) { return static_cast<size_t> (0); } | |||||
| #define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); | #define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); | ||||
| #define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); | #define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); | ||||
| #define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); | #define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); | ||||
| @@ -219,26 +222,26 @@ private: | |||||
| #define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; | #define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; | ||||
| #define DECLARE_JNI_CALLBACK(fieldID, stringName, signature) | #define DECLARE_JNI_CALLBACK(fieldID, stringName, signature) | ||||
| #define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData, byteCodeSize) \ | |||||
| class CppClassName ## _Class : public JNIClassBase \ | |||||
| { \ | |||||
| public: \ | |||||
| CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, byteCodeSize) {} \ | |||||
| \ | |||||
| void initialiseFields (JNIEnv* env) \ | |||||
| { \ | |||||
| Array<JNINativeMethod> callbacks; \ | |||||
| JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \ | |||||
| resolveCallbacks (env, callbacks); \ | |||||
| } \ | |||||
| \ | |||||
| JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \ | |||||
| }; \ | |||||
| static CppClassName ## _Class CppClassName; | |||||
| #define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData) \ | |||||
| class CppClassName ## _Class : public JNIClassBase \ | |||||
| { \ | |||||
| public: \ | |||||
| CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, numBytes (byteCodeData)) {} \ | |||||
| \ | |||||
| void initialiseFields (JNIEnv* env) \ | |||||
| { \ | |||||
| Array<JNINativeMethod> callbacks; \ | |||||
| JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \ | |||||
| resolveCallbacks (env, callbacks); \ | |||||
| } \ | |||||
| \ | |||||
| JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \ | |||||
| }; \ | |||||
| static inline const CppClassName ## _Class CppClassName; | |||||
| //============================================================================== | //============================================================================== | ||||
| #define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \ | #define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \ | ||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr, 0) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr) | |||||
| //============================================================================== | //============================================================================== | ||||
| #define DECLARE_JNI_CLASS(CppClassName, javaPath) \ | #define DECLARE_JNI_CLASS(CppClassName, javaPath) \ | ||||
| @@ -1005,20 +1008,20 @@ public: | |||||
| const Array<int>& /*grantResults*/) {} | const Array<int>& /*grantResults*/) {} | ||||
| virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef<jobject> /*data*/) {} | virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef<jobject> /*data*/) {} | ||||
| /** @internal */ | |||||
| static void onCreatedCallback (JNIEnv*, FragmentOverlay&, jobject obj); | |||||
| /** @internal */ | |||||
| static void onStartCallback (JNIEnv*, FragmentOverlay&); | |||||
| /** @internal */ | |||||
| static void onRequestPermissionsResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jobjectArray jPermissions, jintArray jGrantResults); | |||||
| /** @internal */ | |||||
| static void onActivityResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jint resultCode, jobject data); | |||||
| protected: | protected: | ||||
| jobject getNativeHandle(); | jobject getNativeHandle(); | ||||
| private: | private: | ||||
| GlobalRef native; | GlobalRef native; | ||||
| public: | |||||
| /* internal: do not use */ | |||||
| static void onActivityResultNative (JNIEnv*, jobject, jlong, jint, jint, jobject); | |||||
| static void onCreateNative (JNIEnv*, jobject, jlong, jobject); | |||||
| static void onStartNative (JNIEnv*, jobject, jlong); | |||||
| static void onRequestPermissionsResultNative (JNIEnv*, jobject, jlong, jint, | |||||
| jobjectArray, jintArray); | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1030,4 +1033,30 @@ void startAndroidActivityForResult (const LocalRef<jobject>& intent, int request | |||||
| bool androidHasSystemFeature (const String& property); | bool androidHasSystemFeature (const String& property); | ||||
| String audioManagerGetProperty (const String& property); | String audioManagerGetProperty (const String& property); | ||||
| namespace detail | |||||
| { | |||||
| template <auto Fn, typename Result, typename Class, typename... Args> | |||||
| inline constexpr auto generatedCallbackImpl = | |||||
| juce::toFnPtr (JNICALL [] (JNIEnv* env, jobject, jlong host, Args... args) -> Result | |||||
| { | |||||
| if (auto* object = reinterpret_cast<Class*> (host)) | |||||
| return Fn (env, *object, args...); | |||||
| return {}; | |||||
| }); | |||||
| template <auto Fn, typename Result, typename Class, typename... Args> | |||||
| constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, Class&, Args...)) { return generatedCallbackImpl<Fn, Result, Class, Args...>; } | |||||
| template <auto Fn, typename Result, typename Class, typename... Args> | |||||
| constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, const Class&, Args...)) { return generatedCallbackImpl<Fn, Result, Class, Args...>; } | |||||
| } // namespace detail | |||||
| // Evaluates to a static function that forwards to the provided Fn, assuming that the | |||||
| // 'host' argument points to an object on which it is valid to call Fn | |||||
| template <auto Fn> | |||||
| inline constexpr auto generatedCallback = detail::generateCallbackImpl<Fn> (Fn); | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -195,7 +195,7 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer") | |||||
| METHOD (isExhausted, "isExhausted", "()Z") \ | METHOD (isExhausted, "isExhausted", "()Z") \ | ||||
| METHOD (setPosition, "setPosition", "(J)Z") \ | METHOD (setPosition, "setPosition", "(J)Z") \ | ||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -342,15 +342,6 @@ namespace detail | |||||
| { | { | ||||
| return joinCompileTimeStr (v, makeCompileTimeStr (others...)); | return joinCompileTimeStr (v, makeCompileTimeStr (others...)); | ||||
| } | } | ||||
| template <typename Functor, typename Return, typename... Args> | |||||
| static constexpr auto toFnPtr (Functor functor, Return (Functor::*) (Args...) const) | |||||
| { | |||||
| return static_cast<Return (*) (Args...)> (functor); | |||||
| } | |||||
| template <typename Functor> | |||||
| static constexpr auto toFnPtr (Functor functor) { return toFnPtr (functor, &Functor::operator()); } | |||||
| } // namespace detail | } // namespace detail | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -396,7 +387,7 @@ struct ObjCClass | |||||
| } | } | ||||
| template <typename Fn> | template <typename Fn> | ||||
| void addMethod (SEL selector, Fn callbackFn) { addMethod (selector, detail::toFnPtr (callbackFn)); } | |||||
| void addMethod (SEL selector, Fn callbackFn) { addMethod (selector, toFnPtr (callbackFn)); } | |||||
| template <typename Result, typename... Args> | template <typename Result, typename... Args> | ||||
| void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) | void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) | ||||
| @@ -336,6 +336,28 @@ namespace juce | |||||
| #include "native/juce_linux_FileChooser.cpp" | #include "native/juce_linux_FileChooser.cpp" | ||||
| #elif JUCE_ANDROID | #elif JUCE_ANDROID | ||||
| namespace juce | |||||
| { | |||||
| static jobject makeAndroidRect (Rectangle<int> r) | |||||
| { | |||||
| return getEnv()->NewObject (AndroidRect, | |||||
| AndroidRect.constructor, | |||||
| r.getX(), | |||||
| r.getY(), | |||||
| r.getRight(), | |||||
| r.getBottom()); | |||||
| } | |||||
| static jobject makeAndroidPoint (Point<int> p) | |||||
| { | |||||
| return getEnv()->NewObject (AndroidPoint, | |||||
| AndroidPoint.create, | |||||
| p.getX(), | |||||
| p.getY()); | |||||
| } | |||||
| } // namespace juce | |||||
| #include "juce_core/files/juce_common_MimeTypes.h" | #include "juce_core/files/juce_common_MimeTypes.h" | ||||
| #include "native/accessibility/juce_android_Accessibility.cpp" | #include "native/accessibility/juce_android_Accessibility.cpp" | ||||
| #include "native/juce_android_Windowing.cpp" | #include "native/juce_android_Windowing.cpp" | ||||
| @@ -26,7 +26,6 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| //============================================================================== | |||||
| /** | /** | ||||
| An abstract base class which can be implemented by components that function as | An abstract base class which can be implemented by components that function as | ||||
| text editors. | text editors. | ||||
| @@ -297,21 +297,13 @@ public: | |||||
| { | { | ||||
| const auto scale = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale; | const auto scale = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale; | ||||
| const auto screenBounds = accessibilityHandler.getComponent().getScreenBounds() * scale; | |||||
| LocalRef<jobject> screenBounds (makeAndroidRect (accessibilityHandler.getComponent().getScreenBounds() * scale)); | |||||
| LocalRef<jobject> rect (env->NewObject (AndroidRect, AndroidRect.constructor, | |||||
| screenBounds.getX(), screenBounds.getY(), | |||||
| screenBounds.getRight(), screenBounds.getBottom())); | |||||
| env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInScreen, screenBounds.get()); | |||||
| env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInScreen, rect.get()); | |||||
| LocalRef<jobject> boundsInParent (makeAndroidRect (accessibilityHandler.getComponent().getBoundsInParent() * scale)); | |||||
| const auto boundsInParent = accessibilityHandler.getComponent().getBoundsInParent() * scale; | |||||
| rect = LocalRef<jobject> (env->NewObject (AndroidRect, AndroidRect.constructor, | |||||
| boundsInParent.getX(), boundsInParent.getY(), | |||||
| boundsInParent.getRight(), boundsInParent.getBottom())); | |||||
| env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInParent, rect.get()); | |||||
| env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInParent, boundsInParent.get()); | |||||
| } | } | ||||
| const auto state = accessibilityHandler.getCurrentState(); | const auto state = accessibilityHandler.getCurrentState(); | ||||
| @@ -32,9 +32,19 @@ import android.graphics.Canvas; | |||||
| import android.graphics.ColorMatrix; | import android.graphics.ColorMatrix; | ||||
| import android.graphics.ColorMatrixColorFilter; | import android.graphics.ColorMatrixColorFilter; | ||||
| import android.graphics.Paint; | import android.graphics.Paint; | ||||
| import android.graphics.Point; | |||||
| import android.graphics.Rect; | import android.graphics.Rect; | ||||
| import android.os.Build; | |||||
| import android.text.Selection; | |||||
| import android.text.SpanWatcher; | |||||
| import android.text.Spannable; | |||||
| import android.text.Spanned; | |||||
| import android.text.TextWatcher; | |||||
| import android.util.Pair; | |||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.text.Editable; | |||||
| import android.text.InputType; | import android.text.InputType; | ||||
| import android.text.SpannableStringBuilder; | |||||
| import android.view.Choreographer; | import android.view.Choreographer; | ||||
| import android.view.KeyEvent; | import android.view.KeyEvent; | ||||
| import android.view.MotionEvent; | import android.view.MotionEvent; | ||||
| @@ -51,6 +61,7 @@ import android.view.inputmethod.InputMethodManager; | |||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| public final class ComponentPeerView extends ViewGroup | public final class ComponentPeerView extends ViewGroup | ||||
| @@ -63,9 +74,10 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (Application.class.isInstance (context)) | if (Application.class.isInstance (context)) | ||||
| { | { | ||||
| ((Application) context).registerActivityLifecycleCallbacks (this); | ((Application) context).registerActivityLifecycleCallbacks (this); | ||||
| } else | |||||
| } | |||||
| else | |||||
| { | { | ||||
| ((Application) context.getApplicationContext ()).registerActivityLifecycleCallbacks (this); | |||||
| ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks (this); | |||||
| } | } | ||||
| this.host = host; | this.host = host; | ||||
| @@ -77,7 +89,7 @@ public final class ComponentPeerView extends ViewGroup | |||||
| setOnFocusChangeListener (this); | setOnFocusChangeListener (this); | ||||
| // swap red and blue colours to match internal opengl texture format | // swap red and blue colours to match internal opengl texture format | ||||
| ColorMatrix colorMatrix = new ColorMatrix (); | |||||
| ColorMatrix colorMatrix = new ColorMatrix(); | |||||
| float[] colorTransform = {0, 0, 1.0f, 0, 0, | float[] colorTransform = {0, 0, 1.0f, 0, 0, | ||||
| 0, 1.0f, 0, 0, 0, | 0, 1.0f, 0, 0, 0, | ||||
| @@ -91,10 +103,12 @@ public final class ComponentPeerView extends ViewGroup | |||||
| try | try | ||||
| { | { | ||||
| method = getClass ().getMethod ("setLayerType", int.class, Paint.class); | |||||
| } catch (SecurityException e) | |||||
| method = getClass().getMethod ("setLayerType", int.class, Paint.class); | |||||
| } | |||||
| catch (SecurityException e) | |||||
| { | { | ||||
| } catch (NoSuchMethodException e) | |||||
| } | |||||
| catch (NoSuchMethodException e) | |||||
| { | { | ||||
| } | } | ||||
| @@ -104,11 +118,14 @@ public final class ComponentPeerView extends ViewGroup | |||||
| { | { | ||||
| int layerTypeNone = 0; | int layerTypeNone = 0; | ||||
| method.invoke (this, layerTypeNone, null); | method.invoke (this, layerTypeNone, null); | ||||
| } catch (java.lang.IllegalArgumentException e) | |||||
| } | |||||
| catch (java.lang.IllegalArgumentException e) | |||||
| { | { | ||||
| } catch (java.lang.IllegalAccessException e) | |||||
| } | |||||
| catch (java.lang.IllegalAccessException e) | |||||
| { | { | ||||
| } catch (java.lang.reflect.InvocationTargetException e) | |||||
| } | |||||
| catch (java.lang.reflect.InvocationTargetException e) | |||||
| { | { | ||||
| } | } | ||||
| } | } | ||||
| @@ -116,7 +133,7 @@ public final class ComponentPeerView extends ViewGroup | |||||
| Choreographer.getInstance().postFrameCallback (this); | Choreographer.getInstance().postFrameCallback (this); | ||||
| } | } | ||||
| public void clear () | |||||
| public void clear() | |||||
| { | { | ||||
| host = 0; | host = 0; | ||||
| } | } | ||||
| @@ -147,14 +164,14 @@ public final class ComponentPeerView extends ViewGroup | |||||
| } | } | ||||
| @Override | @Override | ||||
| public boolean isOpaque () | |||||
| public boolean isOpaque() | |||||
| { | { | ||||
| return opaque; | return opaque; | ||||
| } | } | ||||
| private final boolean opaque; | private final boolean opaque; | ||||
| private long host; | private long host; | ||||
| private final Paint paint = new Paint (); | |||||
| private final Paint paint = new Paint(); | |||||
| //============================================================================== | //============================================================================== | ||||
| private native void handleMouseDown (long host, int index, float x, float y, long time); | private native void handleMouseDown (long host, int index, float x, float y, long time); | ||||
| @@ -168,25 +185,25 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (host == 0) | if (host == 0) | ||||
| return false; | return false; | ||||
| int action = event.getAction (); | |||||
| long time = event.getEventTime (); | |||||
| int action = event.getAction(); | |||||
| long time = event.getEventTime(); | |||||
| switch (action & MotionEvent.ACTION_MASK) | switch (action & MotionEvent.ACTION_MASK) | ||||
| { | { | ||||
| case MotionEvent.ACTION_DOWN: | case MotionEvent.ACTION_DOWN: | ||||
| handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); | |||||
| handleMouseDown (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time); | |||||
| return true; | return true; | ||||
| case MotionEvent.ACTION_CANCEL: | case MotionEvent.ACTION_CANCEL: | ||||
| case MotionEvent.ACTION_UP: | case MotionEvent.ACTION_UP: | ||||
| handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); | |||||
| handleMouseUp (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time); | |||||
| return true; | return true; | ||||
| case MotionEvent.ACTION_MOVE: | case MotionEvent.ACTION_MOVE: | ||||
| { | { | ||||
| handleMouseDrag (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); | |||||
| handleMouseDrag (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time); | |||||
| int n = event.getPointerCount (); | |||||
| int n = event.getPointerCount(); | |||||
| if (n > 1) | if (n > 1) | ||||
| { | { | ||||
| @@ -206,8 +223,9 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (i == 0) | if (i == 0) | ||||
| { | { | ||||
| handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); | |||||
| } else | |||||
| handleMouseUp (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| int point[] = new int[2]; | int point[] = new int[2]; | ||||
| getLocationOnScreen (point); | getLocationOnScreen (point); | ||||
| @@ -223,8 +241,9 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (i == 0) | if (i == 0) | ||||
| { | { | ||||
| handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); | |||||
| } else | |||||
| handleMouseDown (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| int point[] = new int[2]; | int point[] = new int[2]; | ||||
| getLocationOnScreen (point); | getLocationOnScreen (point); | ||||
| @@ -253,32 +272,132 @@ public final class ComponentPeerView extends ViewGroup | |||||
| return false; | return false; | ||||
| } | } | ||||
| //============================================================================== | |||||
| public static class TextInputTarget | |||||
| { | |||||
| public TextInputTarget (long owner) { host = owner; } | |||||
| public boolean isTextInputActive() { return ComponentPeerView.textInputTargetIsTextInputActive (host); } | |||||
| public int getHighlightedRegionBegin() { return ComponentPeerView.textInputTargetGetHighlightedRegionBegin (host); } | |||||
| public int getHighlightedRegionEnd() { return ComponentPeerView.textInputTargetGetHighlightedRegionEnd (host); } | |||||
| public void setHighlightedRegion (int b, int e) { ComponentPeerView.textInputTargetSetHighlightedRegion (host, b, e); } | |||||
| public String getTextInRange (int b, int e) { return ComponentPeerView.textInputTargetGetTextInRange (host, b, e); } | |||||
| public void insertTextAtCaret (String text) { ComponentPeerView.textInputTargetInsertTextAtCaret (host, text); } | |||||
| public int getCaretPosition() { return ComponentPeerView.textInputTargetGetCaretPosition (host); } | |||||
| public int getTotalNumChars() { return ComponentPeerView.textInputTargetGetTotalNumChars (host); } | |||||
| public int getCharIndexForPoint (Point point) { return ComponentPeerView.textInputTargetGetCharIndexForPoint (host, point); } | |||||
| public int getKeyboardType() { return ComponentPeerView.textInputTargetGetKeyboardType (host); } | |||||
| public void setTemporaryUnderlining (List<Pair<Integer, Integer>> list) { ComponentPeerView.textInputTargetSetTemporaryUnderlining (host, list); } | |||||
| //============================================================================== | |||||
| private final long host; | |||||
| } | |||||
| private native static boolean textInputTargetIsTextInputActive (long host); | |||||
| private native static int textInputTargetGetHighlightedRegionBegin (long host); | |||||
| private native static int textInputTargetGetHighlightedRegionEnd (long host); | |||||
| private native static void textInputTargetSetHighlightedRegion (long host, int begin, int end); | |||||
| private native static String textInputTargetGetTextInRange (long host, int begin, int end); | |||||
| private native static void textInputTargetInsertTextAtCaret (long host, String text); | |||||
| private native static int textInputTargetGetCaretPosition (long host); | |||||
| private native static int textInputTargetGetTotalNumChars (long host); | |||||
| private native static int textInputTargetGetCharIndexForPoint (long host, Point point); | |||||
| private native static int textInputTargetGetKeyboardType (long host); | |||||
| private native static void textInputTargetSetTemporaryUnderlining (long host, List<Pair<Integer, Integer>> list); | |||||
| private native long getFocusedTextInputTargetPointer (long host); | |||||
| private TextInputTarget getFocusedTextInputTarget (long host) | |||||
| { | |||||
| final long ptr = getFocusedTextInputTargetPointer (host); | |||||
| return ptr != 0 ? new TextInputTarget (ptr) : null; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| private native void handleKeyDown (long host, int keycode, int textchar, int kbFlags); | private native void handleKeyDown (long host, int keycode, int textchar, int kbFlags); | ||||
| private native void handleKeyUp (long host, int keycode, int textchar); | private native void handleKeyUp (long host, int keycode, int textchar); | ||||
| private native void handleBackButton (long host); | private native void handleBackButton (long host); | ||||
| private native void handleKeyboardHidden (long host); | private native void handleKeyboardHidden (long host); | ||||
| public void showKeyboard (String type) | |||||
| private static int getInputTypeForJuceVirtualKeyboardType (int type) | |||||
| { | { | ||||
| InputMethodManager imm = (InputMethodManager) getContext ().getSystemService (Context.INPUT_METHOD_SERVICE); | |||||
| if (imm != null) | |||||
| switch (type) | |||||
| { | { | ||||
| if (type.length () > 0) | |||||
| { | |||||
| imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); | |||||
| imm.setInputMethod (getWindowToken (), type); | |||||
| keyboardDismissListener.startListening (); | |||||
| } else | |||||
| { | |||||
| imm.hideSoftInputFromWindow (getWindowToken (), 0); | |||||
| keyboardDismissListener.stopListening (); | |||||
| } | |||||
| case 0: // textKeyboard | |||||
| return InputType.TYPE_CLASS_TEXT | |||||
| | InputType.TYPE_TEXT_VARIATION_NORMAL | |||||
| | InputType.TYPE_TEXT_FLAG_MULTI_LINE | |||||
| | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |||||
| case 1: // numericKeyboard | |||||
| return InputType.TYPE_CLASS_NUMBER | |||||
| | InputType.TYPE_NUMBER_VARIATION_NORMAL; | |||||
| case 2: // decimalKeyboard | |||||
| return InputType.TYPE_CLASS_NUMBER | |||||
| | InputType.TYPE_NUMBER_VARIATION_NORMAL | |||||
| | InputType.TYPE_NUMBER_FLAG_DECIMAL; | |||||
| case 3: // urlKeyboard | |||||
| return InputType.TYPE_CLASS_TEXT | |||||
| | InputType.TYPE_TEXT_VARIATION_URI | |||||
| | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |||||
| case 4: // emailAddressKeyboard | |||||
| return InputType.TYPE_CLASS_TEXT | |||||
| | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | |||||
| | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |||||
| case 5: // phoneNumberKeyboard | |||||
| return InputType.TYPE_CLASS_PHONE; | |||||
| case 6: // passwordKeyboard | |||||
| return InputType.TYPE_CLASS_TEXT | |||||
| | InputType.TYPE_TEXT_VARIATION_PASSWORD | |||||
| | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; | |||||
| } | } | ||||
| return 0; | |||||
| } | |||||
| InputMethodManager getInputMethodManager() | |||||
| { | |||||
| return (InputMethodManager) getContext().getSystemService (Context.INPUT_METHOD_SERVICE); | |||||
| } | |||||
| public void closeInputMethodContext() | |||||
| { | |||||
| InputMethodManager imm = getInputMethodManager(); | |||||
| if (imm == null) | |||||
| return; | |||||
| if (cachedConnection != null) | |||||
| cachedConnection.closeConnection(); | |||||
| imm.restartInput (this); | |||||
| } | |||||
| public void showKeyboard (int virtualKeyboardType, int selectionStart, int selectionEnd) | |||||
| { | |||||
| InputMethodManager imm = getInputMethodManager(); | |||||
| if (imm == null) | |||||
| return; | |||||
| // restartingInput causes a call back to onCreateInputConnection, where we'll pick | |||||
| // up the correct keyboard characteristics to use for the focused TextInputTarget. | |||||
| imm.restartInput (this); | |||||
| imm.showSoftInput (this, 0); | |||||
| keyboardDismissListener.startListening(); | |||||
| } | } | ||||
| public void backButtonPressed () | |||||
| public void hideKeyboard() | |||||
| { | |||||
| InputMethodManager imm = getInputMethodManager(); | |||||
| if (imm == null) | |||||
| return; | |||||
| imm.hideSoftInputFromWindow (getWindowToken(), 0); | |||||
| keyboardDismissListener.stopListening(); | |||||
| } | |||||
| public void backButtonPressed() | |||||
| { | { | ||||
| if (host == 0) | if (host == 0) | ||||
| return; | return; | ||||
| @@ -292,6 +411,11 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (host == 0) | if (host == 0) | ||||
| return false; | return false; | ||||
| // The key event may move the cursor, or in some cases it might enter characters (e.g. | |||||
| // digits). In this case, we need to reset the IME so that it's aware of the new contents | |||||
| // of the TextInputTarget. | |||||
| closeInputMethodContext(); | |||||
| switch (keyCode) | switch (keyCode) | ||||
| { | { | ||||
| case KeyEvent.KEYCODE_VOLUME_UP: | case KeyEvent.KEYCODE_VOLUME_UP: | ||||
| @@ -299,7 +423,7 @@ public final class ComponentPeerView extends ViewGroup | |||||
| return super.onKeyDown (keyCode, event); | return super.onKeyDown (keyCode, event); | ||||
| case KeyEvent.KEYCODE_BACK: | case KeyEvent.KEYCODE_BACK: | ||||
| { | { | ||||
| backButtonPressed (); | |||||
| backButtonPressed(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -309,8 +433,9 @@ public final class ComponentPeerView extends ViewGroup | |||||
| handleKeyDown (host, | handleKeyDown (host, | ||||
| keyCode, | keyCode, | ||||
| event.getUnicodeChar (), | |||||
| event.getMetaState ()); | |||||
| event.getUnicodeChar(), | |||||
| event.getMetaState()); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -320,7 +445,7 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (host == 0) | if (host == 0) | ||||
| return false; | return false; | ||||
| handleKeyUp (host, keyCode, event.getUnicodeChar ()); | |||||
| handleKeyUp (host, keyCode, event.getUnicodeChar()); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -330,17 +455,17 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (host == 0) | if (host == 0) | ||||
| return false; | return false; | ||||
| if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction () != KeyEvent.ACTION_MULTIPLE) | |||||
| if (keyCode != KeyEvent.KEYCODE_UNKNOWN || (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && event.getAction() != KeyEvent.ACTION_MULTIPLE)) | |||||
| return super.onKeyMultiple (keyCode, count, event); | return super.onKeyMultiple (keyCode, count, event); | ||||
| if (event.getCharacters () != null) | |||||
| if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && event.getCharacters() != null) | |||||
| { | { | ||||
| int utf8Char = event.getCharacters ().codePointAt (0); | |||||
| int utf8Char = event.getCharacters().codePointAt (0); | |||||
| handleKeyDown (host, | handleKeyDown (host, | ||||
| keyCode, | keyCode, | ||||
| utf8Char, | utf8Char, | ||||
| event.getMetaState ()); | |||||
| event.getMetaState()); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -355,39 +480,40 @@ public final class ComponentPeerView extends ViewGroup | |||||
| view = viewToUse; | view = viewToUse; | ||||
| } | } | ||||
| private void startListening () | |||||
| private void startListening() | |||||
| { | { | ||||
| view.getViewTreeObserver ().addOnGlobalLayoutListener (viewTreeObserver); | |||||
| view.getViewTreeObserver().addOnGlobalLayoutListener (viewTreeObserver); | |||||
| } | } | ||||
| private void stopListening () | |||||
| private void stopListening() | |||||
| { | { | ||||
| view.getViewTreeObserver ().removeGlobalOnLayoutListener (viewTreeObserver); | |||||
| view.getViewTreeObserver().removeOnGlobalLayoutListener (viewTreeObserver); | |||||
| } | } | ||||
| private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener | private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener | ||||
| { | { | ||||
| TreeObserver () | |||||
| TreeObserver() | |||||
| { | { | ||||
| keyboardShown = false; | keyboardShown = false; | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void onGlobalLayout () | |||||
| public void onGlobalLayout() | |||||
| { | { | ||||
| Rect r = new Rect (); | |||||
| Rect r = new Rect(); | |||||
| View parentView = getRootView (); | |||||
| View parentView = getRootView(); | |||||
| int diff; | int diff; | ||||
| if (parentView == null) | if (parentView == null) | ||||
| { | { | ||||
| getWindowVisibleDisplayFrame (r); | getWindowVisibleDisplayFrame (r); | ||||
| diff = getHeight () - (r.bottom - r.top); | |||||
| } else | |||||
| diff = getHeight() - (r.bottom - r.top); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| parentView.getWindowVisibleDisplayFrame (r); | parentView.getWindowVisibleDisplayFrame (r); | ||||
| diff = parentView.getHeight () - (r.bottom - r.top); | |||||
| diff = parentView.getHeight() - (r.bottom - r.top); | |||||
| } | } | ||||
| // Arbitrary threshold, surely keyboard would take more than 20 pix. | // Arbitrary threshold, surely keyboard would take more than 20 pix. | ||||
| @@ -397,7 +523,7 @@ public final class ComponentPeerView extends ViewGroup | |||||
| handleKeyboardHidden (view.host); | handleKeyboardHidden (view.host); | ||||
| } | } | ||||
| if (!keyboardShown && diff > 20) | |||||
| if (! keyboardShown && diff > 20) | |||||
| keyboardShown = true; | keyboardShown = true; | ||||
| } | } | ||||
| @@ -405,26 +531,219 @@ public final class ComponentPeerView extends ViewGroup | |||||
| } | } | ||||
| private final ComponentPeerView view; | private final ComponentPeerView view; | ||||
| private final TreeObserver viewTreeObserver = new TreeObserver (); | |||||
| private final TreeObserver viewTreeObserver = new TreeObserver(); | |||||
| } | } | ||||
| private final KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this); | private final KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this); | ||||
| // this is here to make keyboard entry work on a Galaxy Tab2 10.1 | |||||
| //============================================================================== | |||||
| // This implementation is quite similar to the ChangeListener in Android's built-in TextView. | |||||
| private static final class ChangeWatcher implements SpanWatcher, TextWatcher | |||||
| { | |||||
| public ChangeWatcher (ComponentPeerView viewIn, Editable editableIn, TextInputTarget targetIn) | |||||
| { | |||||
| view = viewIn; | |||||
| editable = editableIn; | |||||
| target = targetIn; | |||||
| updateEditableSelectionFromTarget (editable, target); | |||||
| } | |||||
| @Override | |||||
| public void onSpanAdded (Spannable text, Object what, int start, int end) | |||||
| { | |||||
| updateTargetRangesFromEditable (editable, target); | |||||
| } | |||||
| @Override | |||||
| public void onSpanRemoved (Spannable text, Object what, int start, int end) | |||||
| { | |||||
| updateTargetRangesFromEditable (editable, target); | |||||
| } | |||||
| @Override | |||||
| public void onSpanChanged (Spannable text, Object what, int ostart, int oend, int nstart, int nend) | |||||
| { | |||||
| updateTargetRangesFromEditable (editable, target); | |||||
| } | |||||
| @Override | |||||
| public void afterTextChanged (Editable s) | |||||
| { | |||||
| } | |||||
| @Override | |||||
| public void beforeTextChanged (CharSequence s, int start, int count, int after) | |||||
| { | |||||
| contentsBeforeChange = s.toString(); | |||||
| } | |||||
| @Override | |||||
| public void onTextChanged (CharSequence s, int start, int before, int count) | |||||
| { | |||||
| if (editable != s || contentsBeforeChange == null) | |||||
| return; | |||||
| final String newText = s.subSequence (start, start + count).toString(); | |||||
| int code = 0; | |||||
| if (newText.endsWith ("\n") || newText.endsWith ("\r")) | |||||
| code = KeyEvent.KEYCODE_ENTER; | |||||
| if (newText.endsWith ("\t")) | |||||
| code = KeyEvent.KEYCODE_TAB; | |||||
| target.setHighlightedRegion (contentsBeforeChange.codePointCount (0, start), | |||||
| contentsBeforeChange.codePointCount (0, start + before)); | |||||
| target.insertTextAtCaret (code != 0 ? newText.substring (0, newText.length() - 1) | |||||
| : newText); | |||||
| // Treating return/tab as individual keypresses rather than part of the composition | |||||
| // sequence allows TextEditor onReturn and onTab to work as expected. | |||||
| if (code != 0) | |||||
| view.onKeyDown (code, new KeyEvent (KeyEvent.ACTION_DOWN, code)); | |||||
| updateTargetRangesFromEditable (editable, target); | |||||
| contentsBeforeChange = null; | |||||
| } | |||||
| private static void updateEditableSelectionFromTarget (Editable editable, TextInputTarget text) | |||||
| { | |||||
| final int start = text.getHighlightedRegionBegin(); | |||||
| final int end = text.getHighlightedRegionEnd(); | |||||
| if (start < 0 || end < 0) | |||||
| return; | |||||
| final String string = editable.toString(); | |||||
| Selection.setSelection (editable, | |||||
| string.offsetByCodePoints (0, start), | |||||
| string.offsetByCodePoints (0, end)); | |||||
| } | |||||
| private static void updateTargetSelectionFromEditable (Editable editable, TextInputTarget target) | |||||
| { | |||||
| final int start = Selection.getSelectionStart (editable); | |||||
| final int end = Selection.getSelectionEnd (editable); | |||||
| if (start < 0 || end < 0) | |||||
| return; | |||||
| final String string = editable.toString(); | |||||
| target.setHighlightedRegion (string.codePointCount (0, start), | |||||
| string.codePointCount (0, end)); | |||||
| } | |||||
| private static List<Pair<Integer, Integer>> getUnderlinedRanges (Editable editable) | |||||
| { | |||||
| final int start = BaseInputConnection.getComposingSpanStart (editable); | |||||
| final int end = BaseInputConnection.getComposingSpanEnd (editable); | |||||
| if (start < 0 || end < 0) | |||||
| return null; | |||||
| final String string = editable.toString(); | |||||
| final ArrayList<Pair<Integer, Integer>> pairs = new ArrayList<>(); | |||||
| pairs.add (new Pair<> (string.codePointCount (0, start), string.codePointCount (0, end))); | |||||
| return pairs; | |||||
| } | |||||
| private static void updateTargetCompositionRangesFromEditable (Editable editable, TextInputTarget target) | |||||
| { | |||||
| target.setTemporaryUnderlining (getUnderlinedRanges (editable)); | |||||
| } | |||||
| private static void updateTargetRangesFromEditable (Editable editable, TextInputTarget target) | |||||
| { | |||||
| updateTargetSelectionFromEditable (editable, target); | |||||
| updateTargetCompositionRangesFromEditable (editable, target); | |||||
| } | |||||
| private final ComponentPeerView view; | |||||
| private final TextInputTarget target; | |||||
| private final Editable editable; | |||||
| private String contentsBeforeChange; | |||||
| } | |||||
| private static final class Connection extends BaseInputConnection | |||||
| { | |||||
| Connection (ComponentPeerView viewIn, boolean fullEditor, TextInputTarget targetIn) | |||||
| { | |||||
| super (viewIn, fullEditor); | |||||
| view = viewIn; | |||||
| target = targetIn; | |||||
| } | |||||
| @Override | |||||
| public Editable getEditable() | |||||
| { | |||||
| if (cached != null) | |||||
| return cached; | |||||
| if (target == null) | |||||
| return cached = super.getEditable(); | |||||
| int length = target.getTotalNumChars(); | |||||
| String initialText = target.getTextInRange (0, length); | |||||
| cached = new SpannableStringBuilder (initialText); | |||||
| // Span the entire range of text, so that we pick up changes at any location. | |||||
| // Use cached.length rather than target.getTotalNumChars here, because this | |||||
| // range is in UTF-16 code units, rather than code points. | |||||
| changeWatcher = new ChangeWatcher (view, cached, target); | |||||
| cached.setSpan (changeWatcher, 0, cached.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); | |||||
| return cached; | |||||
| } | |||||
| /** Call this to stop listening for selection/composition updates. | |||||
| We do this before closing the current input method context (e.g. when the user | |||||
| taps on a text view to move the cursor), because otherwise the input system | |||||
| might send another round of notifications *during* the restartInput call, after we've | |||||
| requested that the input session should end. | |||||
| */ | |||||
| @Override | |||||
| public void closeConnection() | |||||
| { | |||||
| if (cached != null && changeWatcher != null) | |||||
| cached.removeSpan (changeWatcher); | |||||
| cached = null; | |||||
| target = null; | |||||
| super.closeConnection(); | |||||
| } | |||||
| private ComponentPeerView view; | |||||
| private TextInputTarget target; | |||||
| private Editable cached; | |||||
| private ChangeWatcher changeWatcher; | |||||
| } | |||||
| @Override | @Override | ||||
| public InputConnection onCreateInputConnection (EditorInfo outAttrs) | public InputConnection onCreateInputConnection (EditorInfo outAttrs) | ||||
| { | { | ||||
| TextInputTarget focused = getFocusedTextInputTarget (host); | |||||
| outAttrs.actionLabel = ""; | outAttrs.actionLabel = ""; | ||||
| outAttrs.hintText = ""; | outAttrs.hintText = ""; | ||||
| outAttrs.initialCapsMode = 0; | outAttrs.initialCapsMode = 0; | ||||
| outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; | |||||
| outAttrs.initialSelStart = focused != null ? focused.getHighlightedRegionBegin() : -1; | |||||
| outAttrs.initialSelEnd = focused != null ? focused.getHighlightedRegionEnd() : -1; | |||||
| outAttrs.label = ""; | outAttrs.label = ""; | ||||
| outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; | |||||
| outAttrs.inputType = InputType.TYPE_NULL; | |||||
| return new BaseInputConnection (this, false); | |||||
| outAttrs.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED | |||||
| | EditorInfo.IME_FLAG_NO_EXTRACT_UI | |||||
| | EditorInfo.IME_FLAG_NO_ENTER_ACTION; | |||||
| outAttrs.inputType = focused != null ? getInputTypeForJuceVirtualKeyboardType (focused.getKeyboardType()) | |||||
| : 0; | |||||
| cachedConnection = new Connection (this, true, focused); | |||||
| return cachedConnection; | |||||
| } | } | ||||
| private Connection cachedConnection; | |||||
| //============================================================================== | //============================================================================== | ||||
| @Override | @Override | ||||
| protected void onSizeChanged (int w, int h, int oldw, int oldh) | protected void onSizeChanged (int w, int h, int oldw, int oldh) | ||||
| @@ -463,11 +782,13 @@ public final class ComponentPeerView extends ViewGroup | |||||
| Method systemUIVisibilityMethod = null; | Method systemUIVisibilityMethod = null; | ||||
| try | try | ||||
| { | { | ||||
| systemUIVisibilityMethod = this.getClass ().getMethod ("setSystemUiVisibility", int.class); | |||||
| } catch (SecurityException e) | |||||
| systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class); | |||||
| } | |||||
| catch (SecurityException e) | |||||
| { | { | ||||
| return; | return; | ||||
| } catch (NoSuchMethodException e) | |||||
| } | |||||
| catch (NoSuchMethodException e) | |||||
| { | { | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -476,18 +797,21 @@ public final class ComponentPeerView extends ViewGroup | |||||
| try | try | ||||
| { | { | ||||
| systemUIVisibilityMethod.invoke (this, visibility); | systemUIVisibilityMethod.invoke (this, visibility); | ||||
| } catch (java.lang.IllegalArgumentException e) | |||||
| } | |||||
| catch (java.lang.IllegalArgumentException e) | |||||
| { | { | ||||
| } catch (java.lang.IllegalAccessException e) | |||||
| } | |||||
| catch (java.lang.IllegalAccessException e) | |||||
| { | { | ||||
| } catch (java.lang.reflect.InvocationTargetException e) | |||||
| } | |||||
| catch (java.lang.reflect.InvocationTargetException e) | |||||
| { | { | ||||
| } | } | ||||
| } | } | ||||
| public boolean isVisible () | |||||
| public boolean isVisible() | |||||
| { | { | ||||
| return getVisibility () == VISIBLE; | |||||
| return getVisibility() == VISIBLE; | |||||
| } | } | ||||
| public void setVisible (boolean b) | public void setVisible (boolean b) | ||||
| @@ -572,11 +896,22 @@ public final class ComponentPeerView extends ViewGroup | |||||
| if (host == 0) | if (host == 0) | ||||
| return null; | return null; | ||||
| final AccessibilityNodeInfo nodeInfo = AccessibilityNodeInfo.obtain (view, virtualViewId); | |||||
| final AccessibilityNodeInfo nodeInfo; | |||||
| if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) | |||||
| { | |||||
| nodeInfo = new AccessibilityNodeInfo (view, virtualViewId); | |||||
| } | |||||
| else | |||||
| { | |||||
| nodeInfo = AccessibilityNodeInfo.obtain (view, virtualViewId); | |||||
| } | |||||
| if (! populateAccessibilityNodeInfo (host, virtualViewId, nodeInfo)) | if (! populateAccessibilityNodeInfo (host, virtualViewId, nodeInfo)) | ||||
| { | { | ||||
| nodeInfo.recycle(); | |||||
| if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) | |||||
| nodeInfo.recycle(); | |||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -617,10 +952,10 @@ public final class ComponentPeerView extends ViewGroup | |||||
| } | } | ||||
| private final JuceAccessibilityNodeProvider nodeProvider = new JuceAccessibilityNodeProvider (this); | private final JuceAccessibilityNodeProvider nodeProvider = new JuceAccessibilityNodeProvider (this); | ||||
| private final AccessibilityManager accessibilityManager = (AccessibilityManager) getContext ().getSystemService (Context.ACCESSIBILITY_SERVICE); | |||||
| private final AccessibilityManager accessibilityManager = (AccessibilityManager) getContext().getSystemService (Context.ACCESSIBILITY_SERVICE); | |||||
| @Override | @Override | ||||
| public AccessibilityNodeProvider getAccessibilityNodeProvider () | |||||
| public AccessibilityNodeProvider getAccessibilityNodeProvider() | |||||
| { | { | ||||
| return nodeProvider; | return nodeProvider; | ||||
| } | } | ||||
| @@ -34,7 +34,6 @@ public final class JuceContentProviderFileObserver extends FileObserver | |||||
| public JuceContentProviderFileObserver (long hostToUse, String path, int mask) | public JuceContentProviderFileObserver (long hostToUse, String path, int mask) | ||||
| { | { | ||||
| super (path, mask); | super (path, mask); | ||||
| host = hostToUse; | host = hostToUse; | ||||
| } | } | ||||
| @@ -101,7 +101,7 @@ public: | |||||
| class Owner | class Owner | ||||
| { | { | ||||
| public: | public: | ||||
| virtual ~Owner() {} | |||||
| virtual ~Owner() = default; | |||||
| virtual void cursorClosed (const AndroidContentSharerCursor&) = 0; | virtual void cursorClosed (const AndroidContentSharerCursor&) = 0; | ||||
| }; | }; | ||||
| @@ -121,9 +121,9 @@ public: | |||||
| jobject getNativeCursor() { return cursor.get(); } | jobject getNativeCursor() { return cursor.get(); } | ||||
| void cursorClosed() | |||||
| static void cursorClosed (JNIEnv*, AndroidContentSharerCursor& t) | |||||
| { | { | ||||
| MessageManager::callAsync ([this] { owner.cursorClosed (*this); }); | |||||
| MessageManager::callAsync ([&t] { t.owner.cursorClosed (t); }); | |||||
| } | } | ||||
| void addRow (LocalRef<jobjectArray>& values) | void addRow (LocalRef<jobjectArray>& values) | ||||
| @@ -141,20 +141,12 @@ private: | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \ | METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \ | ||||
| METHOD (constructor, "<init>", "(J[Ljava/lang/String;)V") \ | METHOD (constructor, "<init>", "(J[Ljava/lang/String;)V") \ | ||||
| CALLBACK (contentSharerCursorClosed, "contentSharerCursorClosed", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&AndroidContentSharerCursor::cursorClosed>, "contentSharerCursorClosed", "(J)V") \ | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderCursor, "com/rmsl/juce/JuceContentProviderCursor", 16, javaJuceContentProviderCursor, sizeof (javaJuceContentProviderCursor)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderCursor, "com/rmsl/juce/JuceContentProviderCursor", 16, javaJuceContentProviderCursor) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| static void JNICALL contentSharerCursorClosed (JNIEnv*, jobject, jlong host) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<AndroidContentSharerCursor*> (host)) | |||||
| myself->cursorClosed(); | |||||
| } | |||||
| }; | }; | ||||
| AndroidContentSharerCursor::JuceContentProviderCursor_Class AndroidContentSharerCursor::JuceContentProviderCursor; | |||||
| //============================================================================== | //============================================================================== | ||||
| class AndroidContentSharerFileObserver | class AndroidContentSharerFileObserver | ||||
| { | { | ||||
| @@ -228,20 +220,17 @@ private: | |||||
| METHOD (constructor, "<init>", "(JLjava/lang/String;I)V") \ | METHOD (constructor, "<init>", "(JLjava/lang/String;I)V") \ | ||||
| METHOD (startWatching, "startWatching", "()V") \ | METHOD (startWatching, "startWatching", "()V") \ | ||||
| METHOD (stopWatching, "stopWatching", "()V") \ | METHOD (stopWatching, "stopWatching", "()V") \ | ||||
| CALLBACK (contentSharerFileObserverEvent, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \ | |||||
| CALLBACK (generatedCallback<&AndroidContentSharerFileObserver::onFileEventCallback>, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \ | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderFileObserver, "com/rmsl/juce/JuceContentProviderFileObserver", 16, javaJuceContentProviderFileObserver, sizeof (javaJuceContentProviderFileObserver)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderFileObserver, "com/rmsl/juce/JuceContentProviderFileObserver", 16, javaJuceContentProviderFileObserver) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| static void JNICALL contentSharerFileObserverEvent (JNIEnv*, jobject /*fileObserver*/, jlong host, int event, jstring path) | |||||
| static void onFileEventCallback (JNIEnv*, AndroidContentSharerFileObserver& t, jint event, jstring path) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<AndroidContentSharerFileObserver*> (host)) | |||||
| myself->onFileEvent (event, LocalRef<jstring> (path)); | |||||
| t.onFileEvent (event, LocalRef<jstring> (path)); | |||||
| } | } | ||||
| }; | }; | ||||
| AndroidContentSharerFileObserver::JuceContentProviderFileObserver_Class AndroidContentSharerFileObserver::JuceContentProviderFileObserver; | |||||
| //============================================================================== | //============================================================================== | ||||
| class AndroidContentSharerPrepareFilesThread : private Thread | class AndroidContentSharerPrepareFilesThread : private Thread | ||||
| { | { | ||||
| @@ -891,6 +880,4 @@ ContentSharer::Pimpl* ContentSharer::createPimpl() | |||||
| return new ContentSharerNativeImpl (*this); | return new ContentSharerNativeImpl (*this); | ||||
| } | } | ||||
| ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider; | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -948,6 +948,11 @@ TextEditor::TextEditor (const String& name, juce_wchar passwordChar) | |||||
| TextEditor::~TextEditor() | TextEditor::~TextEditor() | ||||
| { | { | ||||
| giveAwayKeyboardFocus(); | |||||
| if (auto* peer = getPeer()) | |||||
| peer->refreshTextInputTarget(); | |||||
| textValue.removeListener (textHolder); | textValue.removeListener (textHolder); | ||||
| textValue.referTo (Value()); | textValue.referTo (Value()); | ||||
| @@ -1240,7 +1245,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage) | |||||
| textValue = newText; | textValue = newText; | ||||
| auto oldCursorPos = caretPosition; | auto oldCursorPos = caretPosition; | ||||
| bool cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); | |||||
| auto cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); | |||||
| clearInternal (nullptr); | clearInternal (nullptr); | ||||
| insert (newText, 0, currentFont, findColour (textColourId), nullptr, caretPosition); | insert (newText, 0, currentFont, findColour (textColourId), nullptr, caretPosition); | ||||
| @@ -1376,26 +1381,23 @@ void TextEditor::repaintText (Range<int> range) | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void TextEditor::moveCaret (int newCaretPos) | |||||
| void TextEditor::moveCaret (const int newCaretPos) | |||||
| { | { | ||||
| if (newCaretPos < 0) | |||||
| newCaretPos = 0; | |||||
| else | |||||
| newCaretPos = jmin (newCaretPos, getTotalNumChars()); | |||||
| const auto clamped = std::clamp (newCaretPos, 0, getTotalNumChars()); | |||||
| if (newCaretPos != getCaretPosition()) | |||||
| { | |||||
| caretPosition = newCaretPos; | |||||
| if (clamped == getCaretPosition()) | |||||
| return; | |||||
| if (hasKeyboardFocus (false)) | |||||
| textHolder->restartTimer(); | |||||
| caretPosition = clamped; | |||||
| scrollToMakeSureCursorIsVisible(); | |||||
| updateCaretPosition(); | |||||
| if (hasKeyboardFocus (false)) | |||||
| textHolder->restartTimer(); | |||||
| if (auto* handler = getAccessibilityHandler()) | |||||
| handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged); | |||||
| } | |||||
| scrollToMakeSureCursorIsVisible(); | |||||
| updateCaretPosition(); | |||||
| if (auto* handler = getAccessibilityHandler()) | |||||
| handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged); | |||||
| } | } | ||||
| int TextEditor::getCaretPosition() const | int TextEditor::getCaretPosition() const | ||||
| @@ -182,7 +182,6 @@ bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textChar | |||||
| textCharacter)); | textCharacter)); | ||||
| } | } | ||||
| bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo) | bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo) | ||||
| { | { | ||||
| bool keyWasUsed = false; | bool keyWasUsed = false; | ||||
| @@ -607,7 +606,7 @@ void ComponentPeer::forceDisplayUpdate() | |||||
| Desktop::getInstance().displays->refresh(); | Desktop::getInstance().displays->refresh(); | ||||
| } | } | ||||
| void ComponentPeer::globalFocusChanged (Component*) | |||||
| void ComponentPeer::globalFocusChanged ([[maybe_unused]] Component* comp) | |||||
| { | { | ||||
| refreshTextInputTarget(); | refreshTextInputTarget(); | ||||
| } | } | ||||
| @@ -476,6 +476,11 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons | |||||
| CodeEditorComponent::~CodeEditorComponent() | CodeEditorComponent::~CodeEditorComponent() | ||||
| { | { | ||||
| giveAwayKeyboardFocus(); | |||||
| if (auto* peer = getPeer()) | |||||
| peer->refreshTextInputTarget(); | |||||
| document.removeListener (pimpl.get()); | document.removeListener (pimpl.get()); | ||||
| } | } | ||||
| @@ -1549,8 +1549,6 @@ struct JuceFirebaseInstanceIdService | |||||
| instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token))); | instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token))); | ||||
| } | } | ||||
| }; | }; | ||||
| JuceFirebaseInstanceIdService::InstanceIdService_Class JuceFirebaseInstanceIdService::InstanceIdService; | |||||
| #endif | #endif | ||||
| #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) | #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) | ||||
| @@ -1592,8 +1590,6 @@ struct JuceFirebaseMessagingService | |||||
| LocalRef<jstring> (static_cast<jstring> (error))); | LocalRef<jstring> (static_cast<jstring> (error))); | ||||
| } | } | ||||
| }; | }; | ||||
| JuceFirebaseMessagingService::MessagingService_Class JuceFirebaseMessagingService::MessagingService; | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -29,7 +29,7 @@ namespace juce | |||||
| //============================================================================== | //============================================================================== | ||||
| // This byte-code is generated from native/java/com/rmsl/juce/JuceWebView.java with min sdk version 16 | // This byte-code is generated from native/java/com/rmsl/juce/JuceWebView.java with min sdk version 16 | ||||
| // See juce_core/native/java/README.txt on how to generate this byte-code. | // See juce_core/native/java/README.txt on how to generate this byte-code. | ||||
| static const unsigned char JuceWebView16ByteCode[] = | |||||
| static const uint8 JuceWebView16ByteCode[] = | |||||
| {31,139,8,8,150,114,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,49,54,66,121,116,101,67,111,100,101,46,100,101,120,0,125, | {31,139,8,8,150,114,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,49,54,66,121,116,101,67,111,100,101,46,100,101,120,0,125, | ||||
| 150,93,108,20,85,20,199,207,124,236,78,119,218,110,183,5,74,191,40,109,69,168,72,89,176,162,165,11,88,40,159,101,81,161,88,226, | 150,93,108,20,85,20,199,207,124,236,78,119,218,110,183,5,74,191,40,109,69,168,72,89,176,162,165,11,88,40,159,101,81,161,88,226, | ||||
| 106,34,211,221,107,59,101,118,102,153,153,109,27,67,16,161,137,134,240,96,4,222,72,140,9,18,35,62,18,195,131,15,4,53,250,226,155, | 106,34,211,221,107,59,101,118,102,153,153,109,27,67,16,161,137,134,240,96,4,222,72,140,9,18,35,62,18,195,131,15,4,53,250,226,155, | ||||
| @@ -81,7 +81,7 @@ static const unsigned char JuceWebView16ByteCode[] = | |||||
| //============================================================================== | //============================================================================== | ||||
| // This byte-code is generated from native/javacore/app/com/rmsl/juce/JuceWebView21.java with min sdk version 21 | // This byte-code is generated from native/javacore/app/com/rmsl/juce/JuceWebView21.java with min sdk version 21 | ||||
| // See juce_core/native/java/README.txt on how to generate this byte-code. | // See juce_core/native/java/README.txt on how to generate this byte-code. | ||||
| static const unsigned char JuceWebView21ByteCode[] = | |||||
| static const uint8 JuceWebView21ByteCode[] = | |||||
| {31,139,8,8,45,103,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,50,49,46,100,101,120,0,141,151,93,140,27,87,21,199,207, | {31,139,8,8,45,103,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,50,49,46,100,101,120,0,141,151,93,140,27,87,21,199,207, | ||||
| 204,216,30,219,99,59,182,55,251,145,143,221,110,210,173,178,105,154,186,155,164,52,169,211,106,241,38,219,221,48,41,52,155,108, | 204,216,30,219,99,59,182,55,251,145,143,221,110,210,173,178,105,154,186,155,164,52,169,211,106,241,38,219,221,48,41,52,155,108, | ||||
| 138,43,85,154,181,47,235,73,188,51,206,204,120,119,65,162,132,80,148,138,34,148,168,20,181,125,129,135,16,129,4,18,168,125,136, | 138,43,85,154,181,47,235,73,188,51,206,204,120,119,65,162,132,80,148,138,34,148,168,20,181,125,129,135,16,129,4,18,168,125,136, | ||||
| @@ -479,82 +479,62 @@ private: | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| METHOD (hostDeleted, "hostDeleted", "()V") \ | METHOD (hostDeleted, "hostDeleted", "()V") \ | ||||
| CALLBACK (webViewReceivedHttpError, "webViewReceivedHttpError", "(JLandroid/webkit/WebView;Landroid/webkit/WebResourceRequest;Landroid/webkit/WebResourceResponse;)V") \ | |||||
| CALLBACK (webViewPageLoadStarted, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ | |||||
| CALLBACK (webViewPageLoadFinished, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ | |||||
| CALLBACK (webViewReceivedSslError, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewReceivedHttpError>, "webViewReceivedHttpError", "(JLandroid/webkit/WebView;Landroid/webkit/WebResourceRequest;Landroid/webkit/WebResourceResponse;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewPageLoadStarted>, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewPageLoadFinished>, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewReceivedSslError>, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \ | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient21, "com/rmsl/juce/JuceWebView21$Client", 21, JuceWebView21ByteCode, sizeof (JuceWebView21ByteCode)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient21, "com/rmsl/juce/JuceWebView21$Client", 21, JuceWebView21ByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| METHOD (hostDeleted, "hostDeleted", "()V") \ | METHOD (hostDeleted, "hostDeleted", "()V") \ | ||||
| CALLBACK (webViewPageLoadStarted, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ | |||||
| CALLBACK (webViewPageLoadFinished, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ | |||||
| CALLBACK (webViewReceivedSslError, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewPageLoadStarted>, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewPageLoadFinished>, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewReceivedSslError>, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \ | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient16, "com/rmsl/juce/JuceWebView$Client", 16, JuceWebView16ByteCode, sizeof (JuceWebView16ByteCode)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient16, "com/rmsl/juce/JuceWebView$Client", 16, JuceWebView16ByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| static jboolean JNICALL webViewPageLoadStarted (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jstring url) | |||||
| static jboolean webViewPageLoadStarted (JNIEnv*, Pimpl& t, jstring url) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| return myself->handlePageAboutToLoad (juceString (url)); | |||||
| return 0; | |||||
| return t.handlePageAboutToLoad (juceString (url)); | |||||
| } | } | ||||
| static void JNICALL webViewPageLoadFinished (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jstring url) | |||||
| static void webViewPageLoadFinished (JNIEnv*, Pimpl& t, jstring url) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| myself->owner.pageFinishedLoading (juceString (url)); | |||||
| t.owner.pageFinishedLoading (juceString (url)); | |||||
| } | } | ||||
| static void JNICALL webViewReceivedHttpError (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject errorResponse) | |||||
| static void webViewReceivedSslError (JNIEnv* env, Pimpl& t, jobject sslError) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| myself->webReceivedHttpError (errorResponse); | |||||
| } | |||||
| static void JNICALL webViewReceivedSslError (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*sslErrorHandler*/, jobject sslError) | |||||
| { | |||||
| auto* env = getEnv(); | |||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| { | |||||
| auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sslError, SslError.toString)); | |||||
| myself->owner.pageLoadHadNetworkError (juceString (errorString)); | |||||
| } | |||||
| const auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sslError, SslError.toString)); | |||||
| t.owner.pageLoadHadNetworkError (juceString (errorString)); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| CALLBACK (webViewCloseWindowRequest, "webViewCloseWindowRequest", "(JLandroid/webkit/WebView;)V") \ | |||||
| CALLBACK (webViewCreateWindowRequest, "webViewCreateWindowRequest", "(JLandroid/webkit/WebView;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewCloseWindowRequest>, "webViewCloseWindowRequest", "(JLandroid/webkit/WebView;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::webViewCreateWindowRequest>, "webViewCreateWindowRequest", "(JLandroid/webkit/WebView;)V") \ | |||||
| DECLARE_JNI_CLASS (JuceWebChromeClient, "com/rmsl/juce/JuceWebView$ChromeClient") | DECLARE_JNI_CLASS (JuceWebChromeClient, "com/rmsl/juce/JuceWebView$ChromeClient") | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| static void JNICALL webViewCloseWindowRequest (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/) | |||||
| static void webViewCloseWindowRequest (JNIEnv*, Pimpl& t, jobject) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| myself->owner.windowCloseRequest(); | |||||
| t.owner.windowCloseRequest(); | |||||
| } | } | ||||
| static void JNICALL webViewCreateWindowRequest (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/) | |||||
| static void webViewCreateWindowRequest (JNIEnv*, Pimpl& t, jobject) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) | |||||
| myself->owner.newWindowAttemptingToLoad ({}); | |||||
| t.owner.newWindowAttemptingToLoad ({}); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void webReceivedHttpError (jobject errorResponse) | |||||
| static void webViewReceivedHttpError (JNIEnv* env, Pimpl& t, jobject errorResponse) | |||||
| { | { | ||||
| auto* env = getEnv(); | |||||
| LocalRef<jclass> responseClass (env->FindClass ("android/webkit/WebResourceResponse")); | LocalRef<jclass> responseClass (env->FindClass ("android/webkit/WebResourceResponse")); | ||||
| if (responseClass != nullptr) | if (responseClass != nullptr) | ||||
| @@ -565,14 +545,14 @@ private: | |||||
| { | { | ||||
| auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method)); | auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method)); | ||||
| owner.pageLoadHadNetworkError (juceString (errorString)); | |||||
| t.owner.pageLoadHadNetworkError (juceString (errorString)); | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| // Should never get here! | // Should never get here! | ||||
| jassertfalse; | jassertfalse; | ||||
| owner.pageLoadHadNetworkError ({}); | |||||
| t.owner.pageLoadHadNetworkError ({}); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -596,9 +576,7 @@ WebBrowserComponent::WebBrowserComponent (const Options& options) | |||||
| addAndMakeVisible (browser.get()); | addAndMakeVisible (browser.get()); | ||||
| } | } | ||||
| WebBrowserComponent::~WebBrowserComponent() | |||||
| { | |||||
| } | |||||
| WebBrowserComponent::~WebBrowserComponent() = default; | |||||
| //============================================================================== | //============================================================================== | ||||
| void WebBrowserComponent::goToURL (const String& url, | void WebBrowserComponent::goToURL (const String& url, | ||||
| @@ -728,7 +706,4 @@ bool WebBrowserComponent::areOptionsSupported (const Options& options) | |||||
| return (options.getBackend() == Options::Backend::defaultBackend); | return (options.getBackend() == Options::Backend::defaultBackend); | ||||
| } | } | ||||
| WebBrowserComponent::Pimpl::JuceWebViewClient16_Class WebBrowserComponent::Pimpl::JuceWebViewClient16; | |||||
| WebBrowserComponent::Pimpl::JuceWebViewClient21_Class WebBrowserComponent::Pimpl::JuceWebViewClient21; | |||||
| WebBrowserComponent::Pimpl::JuceWebChromeClient_Class WebBrowserComponent::Pimpl::JuceWebChromeClient; | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -103,26 +103,6 @@ static const uint8 javaJuceOpenGLView[] = | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| struct AndroidGLCallbacks | |||||
| { | |||||
| static void attachedToWindow (JNIEnv*, jobject, jlong); | |||||
| static void detachedFromWindow (JNIEnv*, jobject, jlong); | |||||
| static void dispatchDraw (JNIEnv*, jobject, jlong, jobject); | |||||
| }; | |||||
| //============================================================================== | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||||
| METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \ | |||||
| METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \ | |||||
| METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \ | |||||
| METHOD (layout, "layout", "(IIII)V" ) \ | |||||
| CALLBACK (AndroidGLCallbacks::attachedToWindow, "onAttchedWindowNative", "(J)V") \ | |||||
| CALLBACK (AndroidGLCallbacks::detachedFromWindow, "onDetachedFromWindowNative", "(J)V") \ | |||||
| CALLBACK (AndroidGLCallbacks::dispatchDraw, "onDrawNative", "(JLandroid/graphics/Canvas;)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView, sizeof(javaJuceOpenGLView)) | |||||
| #undef JNI_CLASS_MEMBERS | |||||
| //============================================================================== | //============================================================================== | ||||
| class OpenGLContext::NativeContext : private SurfaceHolderCallback | class OpenGLContext::NativeContext : private SurfaceHolderCallback | ||||
| { | { | ||||
| @@ -266,40 +246,46 @@ public: | |||||
| Component& component; | Component& component; | ||||
| private: | private: | ||||
| //============================================================================== | |||||
| friend struct AndroidGLCallbacks; | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||||
| METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \ | |||||
| METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \ | |||||
| METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \ | |||||
| METHOD (layout, "layout", "(IIII)V" ) \ | |||||
| CALLBACK (generatedCallback<&NativeContext::attachedToWindow>, "onAttchedWindowNative", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&NativeContext::detachedFromWindow>, "onDetachedFromWindowNative", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&NativeContext::dispatchDraw>, "onDrawNative", "(JLandroid/graphics/Canvas;)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView) | |||||
| #undef JNI_CLASS_MEMBERS | |||||
| void attachedToWindow() | |||||
| //============================================================================== | |||||
| static void attachedToWindow (JNIEnv* env, NativeContext& t) | |||||
| { | { | ||||
| auto* env = getEnv(); | |||||
| LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder)); | |||||
| LocalRef<jobject> holder (env->CallObjectMethod (t.surfaceView.get(), JuceOpenGLViewSurface.getHolder)); | |||||
| if (surfaceHolderCallback == nullptr) | |||||
| surfaceHolderCallback = GlobalRef (CreateJavaInterface (this, "android/view/SurfaceHolder$Callback")); | |||||
| if (t.surfaceHolderCallback == nullptr) | |||||
| t.surfaceHolderCallback = GlobalRef (CreateJavaInterface (&t, "android/view/SurfaceHolder$Callback")); | |||||
| env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, surfaceHolderCallback.get()); | |||||
| env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, t.surfaceHolderCallback.get()); | |||||
| } | } | ||||
| void detachedFromWindow() | |||||
| static void detachedFromWindow (JNIEnv* env, NativeContext& t) | |||||
| { | { | ||||
| if (surfaceHolderCallback != nullptr) | |||||
| if (t.surfaceHolderCallback != nullptr) | |||||
| { | { | ||||
| auto* env = getEnv(); | |||||
| LocalRef<jobject> holder (env->CallObjectMethod (t.surfaceView.get(), JuceOpenGLViewSurface.getHolder)); | |||||
| LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder)); | |||||
| env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, surfaceHolderCallback.get()); | |||||
| surfaceHolderCallback.clear(); | |||||
| env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, t.surfaceHolderCallback.get()); | |||||
| t.surfaceHolderCallback.clear(); | |||||
| } | } | ||||
| } | } | ||||
| void dispatchDraw (jobject /*canvas*/) | |||||
| static void dispatchDraw (JNIEnv*, NativeContext& t, jobject /*canvas*/) | |||||
| { | { | ||||
| const std::lock_guard lock { nativeHandleMutex }; | |||||
| const std::lock_guard lock { t.nativeHandleMutex }; | |||||
| if (juceContext != nullptr) | |||||
| juceContext->triggerRepaint(); | |||||
| if (t.juceContext != nullptr) | |||||
| t.juceContext->triggerRepaint(); | |||||
| } | } | ||||
| bool tryChooseConfig (const std::vector<EGLint>& optionalAttribs) | bool tryChooseConfig (const std::vector<EGLint>& optionalAttribs) | ||||
| @@ -414,25 +400,6 @@ private: | |||||
| EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY; | EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY; | ||||
| EGLDisplay OpenGLContext::NativeContext::config; | EGLDisplay OpenGLContext::NativeContext::config; | ||||
| //============================================================================== | |||||
| void AndroidGLCallbacks::attachedToWindow (JNIEnv*, jobject /*this*/, jlong host) | |||||
| { | |||||
| if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host)) | |||||
| nativeContext->attachedToWindow(); | |||||
| } | |||||
| void AndroidGLCallbacks::detachedFromWindow (JNIEnv*, jobject /*this*/, jlong host) | |||||
| { | |||||
| if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host)) | |||||
| nativeContext->detachedFromWindow(); | |||||
| } | |||||
| void AndroidGLCallbacks::dispatchDraw (JNIEnv*, jobject /*this*/, jlong host, jobject canvas) | |||||
| { | |||||
| if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host)) | |||||
| nativeContext->dispatchDraw (canvas); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| bool OpenGLHelpers::isContextActive() | bool OpenGLHelpers::isContextActive() | ||||
| { | { | ||||
| @@ -143,7 +143,7 @@ inline StringArray javaListOfStringToJuceStringArray (const LocalRef<jobject>& j | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| constexpr unsigned char juceBillingClientCompiled[] | |||||
| constexpr uint8 juceBillingClientCompiled[] | |||||
| { | { | ||||
| 0x1f, 0x8b, 0x08, 0x08, 0xa4, 0x53, 0xd0, 0x62, 0x04, 0x03, 0x63, 0x6c, | 0x1f, 0x8b, 0x08, 0x08, 0xa4, 0x53, 0xd0, 0x62, 0x04, 0x03, 0x63, 0x6c, | ||||
| 0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x9d, 0x5a, | 0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x9d, 0x5a, | ||||
| @@ -720,42 +720,17 @@ private: | |||||
| METHOD (queryPurchases, "queryPurchases", "()V") \ | METHOD (queryPurchases, "queryPurchases", "()V") \ | ||||
| METHOD (consumePurchase, "consumePurchase", "(Ljava/lang/String;Ljava/lang/String;)V") \ | METHOD (consumePurchase, "consumePurchase", "(Ljava/lang/String;Ljava/lang/String;)V") \ | ||||
| \ | \ | ||||
| CALLBACK (productDetailsQueryCallback, "productDetailsQueryCallback", "(JLjava/util/List;)V") \ | |||||
| CALLBACK (purchasesListQueryCallback, "purchasesListQueryCallback", "(JLjava/util/List;)V") \ | |||||
| CALLBACK (purchaseCompletedCallback, "purchaseCompletedCallback", "(JLcom/android/billingclient/api/Purchase;I)V") \ | |||||
| CALLBACK (purchaseConsumedCallback, "purchaseConsumedCallback", "(JLjava/lang/String;I)V") | |||||
| CALLBACK (generatedCallback<&Pimpl::updateProductDetails>, "productDetailsQueryCallback", "(JLjava/util/List;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::updatePurchasesList>, "purchasesListQueryCallback", "(JLjava/util/List;)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::purchaseCompleted>, "purchaseCompletedCallback", "(JLcom/android/billingclient/api/Purchase;I)V") \ | |||||
| CALLBACK (generatedCallback<&Pimpl::purchaseConsumed>, "purchaseConsumedCallback", "(JLjava/lang/String;I)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (JuceBillingClient, | DECLARE_JNI_CLASS_WITH_BYTECODE (JuceBillingClient, | ||||
| "com/rmsl/juce/JuceBillingClient", | "com/rmsl/juce/JuceBillingClient", | ||||
| 16, | 16, | ||||
| juceBillingClientCompiled, | |||||
| numElementsInArray (juceBillingClientCompiled)) | |||||
| juceBillingClientCompiled) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| static void JNICALL productDetailsQueryCallback (JNIEnv*, jobject, jlong host, jobject productDetailsList) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<Pimpl*> (host)) | |||||
| myself->updateProductDetails (productDetailsList); | |||||
| } | |||||
| static void JNICALL purchasesListQueryCallback (JNIEnv*, jobject, jlong host, jobject purchasesList) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<Pimpl*> (host)) | |||||
| myself->updatePurchasesList (purchasesList); | |||||
| } | |||||
| static void JNICALL purchaseCompletedCallback (JNIEnv*, jobject, jlong host, jobject purchase, int responseCode) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<Pimpl*> (host)) | |||||
| myself->purchaseCompleted (purchase, responseCode); | |||||
| } | |||||
| static void JNICALL purchaseConsumedCallback (JNIEnv*, jobject, jlong host, jstring productIdentifier, int responseCode) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<Pimpl*> (host)) | |||||
| myself->purchaseConsumed (productIdentifier, responseCode); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| bool isReady() const | bool isReady() const | ||||
| { | { | ||||
| @@ -1095,7 +1070,4 @@ void juce_handleOnResume() | |||||
| }); | }); | ||||
| } | } | ||||
| InAppPurchases::Pimpl::JuceBillingClient_Class InAppPurchases::Pimpl::JuceBillingClient; | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -400,7 +400,7 @@ class MediaRecorderOnInfoListener : public AndroidInterfaceImplementer | |||||
| public: | public: | ||||
| struct Owner | struct Owner | ||||
| { | { | ||||
| virtual ~Owner() {} | |||||
| virtual ~Owner() = default; | |||||
| virtual void onInfo (LocalRef<jobject>& mediaRecorder, int what, int extra) = 0; | virtual void onInfo (LocalRef<jobject>& mediaRecorder, int what, int extra) = 0; | ||||
| }; | }; | ||||
| @@ -1711,26 +1711,25 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(JZ)V") \ | METHOD (constructor, "<init>", "(JZ)V") \ | ||||
| CALLBACK (cameraCaptureSessionCaptureCompletedCallback, "cameraCaptureSessionCaptureCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/TotalCaptureResult;)V") \ | |||||
| CALLBACK (cameraCaptureSessionCaptureFailedCallback, "cameraCaptureSessionCaptureFailed", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/CaptureFailure;)V") \ | |||||
| CALLBACK (cameraCaptureSessionCaptureProgressedCallback, "cameraCaptureSessionCaptureProgressed", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/CaptureResult;)V") \ | |||||
| CALLBACK (cameraCaptureSessionCaptureStartedCallback, "cameraCaptureSessionCaptureStarted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;JJ)V") \ | |||||
| CALLBACK (cameraCaptureSessionCaptureSequenceAbortedCallback, "cameraCaptureSessionCaptureSequenceAborted", "(JZLandroid/hardware/camera2/CameraCaptureSession;I)V") \ | |||||
| CALLBACK (cameraCaptureSessionCaptureSequenceCompletedCallback, "cameraCaptureSessionCaptureSequenceCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;IJ)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (CameraCaptureSessionCaptureCallback, "com/rmsl/juce/CameraCaptureSessionCaptureCallback", 21, CameraSupportByteCode, sizeof(CameraSupportByteCode)) | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureCompletedCallback>, "cameraCaptureSessionCaptureCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/TotalCaptureResult;)V") \ | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureFailedCallback>, "cameraCaptureSessionCaptureFailed", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/CaptureFailure;)V") \ | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureProgressedCallback>, "cameraCaptureSessionCaptureProgressed", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/CaptureResult;)V") \ | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureStartedCallback>, "cameraCaptureSessionCaptureStarted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;JJ)V") \ | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureSequenceAbortedCallback>, "cameraCaptureSessionCaptureSequenceAborted", "(JZLandroid/hardware/camera2/CameraCaptureSession;I)V") \ | |||||
| CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureSequenceCompletedCallback>, "cameraCaptureSessionCaptureSequenceCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;IJ)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (CameraCaptureSessionCaptureCallback, "com/rmsl/juce/CameraCaptureSessionCaptureCallback", 21, CameraSupportByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| LocalRef<jobject> createCaptureSessionCallback (bool createPreviewSession) | LocalRef<jobject> createCaptureSessionCallback (bool createPreviewSession) | ||||
| { | { | ||||
| return LocalRef<jobject>(getEnv()->NewObject (CameraCaptureSessionCaptureCallback, | |||||
| CameraCaptureSessionCaptureCallback.constructor, | |||||
| reinterpret_cast<jlong> (this), | |||||
| createPreviewSession ? 1 : 0)); | |||||
| return LocalRef<jobject> (getEnv()->NewObject (CameraCaptureSessionCaptureCallback, | |||||
| CameraCaptureSessionCaptureCallback.constructor, | |||||
| reinterpret_cast<jlong> (this), | |||||
| createPreviewSession ? 1 : 0)); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| enum class State | enum class State | ||||
| { | { | ||||
| idle = 0, | idle = 0, | ||||
| @@ -2022,71 +2021,53 @@ private: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| static void cameraCaptureSessionCaptureCompletedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| static void cameraCaptureSessionCaptureCompletedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest)); | |||||
| LocalRef<jobject> result (getEnv()->NewLocalRef(rawResult)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| LocalRef<jobject> request (env->NewLocalRef (rawRequest)); | |||||
| LocalRef<jobject> result (env->NewLocalRef (rawResult)); | |||||
| myself->cameraCaptureSessionCaptureCompleted (isPreview != 0, session, request, result); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureCompleted (isPreview != 0, session, request, result); | |||||
| } | } | ||||
| static void cameraCaptureSessionCaptureFailedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| static void cameraCaptureSessionCaptureFailedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest)); | |||||
| LocalRef<jobject> result (getEnv()->NewLocalRef(rawResult)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| LocalRef<jobject> request (env->NewLocalRef (rawRequest)); | |||||
| LocalRef<jobject> result (env->NewLocalRef (rawResult)); | |||||
| myself->cameraCaptureSessionCaptureFailed (isPreview != 0, session, request, result); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureFailed (isPreview != 0, session, request, result); | |||||
| } | } | ||||
| static void cameraCaptureSessionCaptureProgressedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| static void cameraCaptureSessionCaptureProgressedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest)); | |||||
| LocalRef<jobject> result (getEnv()->NewLocalRef(rawResult)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| LocalRef<jobject> request (env->NewLocalRef (rawRequest)); | |||||
| LocalRef<jobject> result (env->NewLocalRef (rawResult)); | |||||
| myself->cameraCaptureSessionCaptureProgressed (isPreview != 0, session, request, result); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureProgressed (isPreview != 0, session, request, result); | |||||
| } | } | ||||
| static void cameraCaptureSessionCaptureSequenceAbortedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jint sequenceId) | |||||
| static void cameraCaptureSessionCaptureSequenceAbortedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jint sequenceId) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| myself->cameraCaptureSessionCaptureSequenceAborted (isPreview != 0, session, sequenceId); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureSequenceAborted (isPreview != 0, session, sequenceId); | |||||
| } | } | ||||
| static void cameraCaptureSessionCaptureSequenceCompletedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jint sequenceId, jlong frameNumber) | |||||
| static void cameraCaptureSessionCaptureSequenceCompletedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jint sequenceId, jlong frameNumber) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| myself->cameraCaptureSessionCaptureSequenceCompleted (isPreview != 0, session, sequenceId, frameNumber); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureSequenceCompleted (isPreview != 0, session, sequenceId, frameNumber); | |||||
| } | } | ||||
| static void cameraCaptureSessionCaptureStartedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jobject rawRequest, jlong timestamp, jlong frameNumber) | |||||
| static void cameraCaptureSessionCaptureStartedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jobject rawRequest, jlong timestamp, jlong frameNumber) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest)); | |||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| LocalRef<jobject> request (env->NewLocalRef (rawRequest)); | |||||
| myself->cameraCaptureSessionCaptureStarted (isPreview != 0, session, request, timestamp, frameNumber); | |||||
| } | |||||
| t.cameraCaptureSessionCaptureStarted (isPreview != 0, session, request, timestamp, frameNumber); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -2115,11 +2096,11 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| CALLBACK(cameraCaptureSessionActiveCallback, "cameraCaptureSessionActive", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK(cameraCaptureSessionClosedCallback, "cameraCaptureSessionClosed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK(cameraCaptureSessionConfigureFailedCallback, "cameraCaptureSessionConfigureFailed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK(cameraCaptureSessionConfiguredCallback, "cameraCaptureSessionConfigured", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK(cameraCaptureSessionReadyCallback, "cameraCaptureSessionReady", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") | |||||
| CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionActiveCallback>, "cameraCaptureSessionActive", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionClosedCallback>, "cameraCaptureSessionClosed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionConfigureFailedCallback>, "cameraCaptureSessionConfigureFailed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionConfiguredCallback>, "cameraCaptureSessionConfigured", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ | |||||
| CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionReadyCallback>, "cameraCaptureSessionReady", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") | |||||
| DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraCaptureSessionStateCallback, "com/rmsl/juce/CameraCaptureSessionStateCallback", 21) | DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraCaptureSessionStateCallback, "com/rmsl/juce/CameraCaptureSessionStateCallback", 21) | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| @@ -2161,124 +2142,83 @@ private: | |||||
| env->CallVoidMethod (captureRequestBuilder, CaptureRequestBuilder.set, jKey.get(), jValue.get()); | env->CallVoidMethod (captureRequestBuilder, CaptureRequestBuilder.set, jKey.get(), jValue.get()); | ||||
| } | } | ||||
| void cameraCaptureSessionActive ([[maybe_unused]] jobject session) | |||||
| //============================================================================== | |||||
| static void cameraCaptureSessionActiveCallback ([[maybe_unused]] JNIEnv* env, | |||||
| [[maybe_unused]] CaptureSession& t, | |||||
| [[maybe_unused]] jobject rawSession) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraCaptureSessionActive()"); | JUCE_CAMERA_LOG ("cameraCaptureSessionActive()"); | ||||
| } | } | ||||
| void cameraCaptureSessionClosed ([[maybe_unused]] jobject session) | |||||
| static void cameraCaptureSessionClosedCallback ([[maybe_unused]] JNIEnv* env, | |||||
| CaptureSession& t, | |||||
| [[maybe_unused]] jobject rawSession) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraCaptureSessionClosed()"); | JUCE_CAMERA_LOG ("cameraCaptureSessionClosed()"); | ||||
| closedEvent.signal(); | |||||
| t.closedEvent.signal(); | |||||
| } | } | ||||
| void cameraCaptureSessionConfigureFailed ([[maybe_unused]] jobject session) | |||||
| static void cameraCaptureSessionConfigureFailedCallback ([[maybe_unused]] JNIEnv* env, | |||||
| CaptureSession& t, | |||||
| [[maybe_unused]] jobject rawSession) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraCaptureSessionConfigureFailed()"); | JUCE_CAMERA_LOG ("cameraCaptureSessionConfigureFailed()"); | ||||
| MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { this }] | |||||
| { | |||||
| if (weakRef != nullptr) | |||||
| weakRef->configuredCallback.captureSessionConfigured (nullptr); | |||||
| }); | |||||
| MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { &t }] | |||||
| { | |||||
| if (weakRef != nullptr) | |||||
| weakRef->configuredCallback.captureSessionConfigured (nullptr); | |||||
| }); | |||||
| } | } | ||||
| void cameraCaptureSessionConfigured (const LocalRef<jobject>& session) | |||||
| static void cameraCaptureSessionConfiguredCallback (JNIEnv* env, CaptureSession& t, jobject rawSession) | |||||
| { | { | ||||
| LocalRef<jobject> session (env->NewLocalRef (rawSession)); | |||||
| JUCE_CAMERA_LOG ("cameraCaptureSessionConfigured()"); | JUCE_CAMERA_LOG ("cameraCaptureSessionConfigured()"); | ||||
| if (pendingClose.get() == 1) | |||||
| if (t.pendingClose.get() == 1) | |||||
| { | { | ||||
| // Already closing, bailout. | // Already closing, bailout. | ||||
| closedEvent.signal(); | |||||
| t.closedEvent.signal(); | |||||
| GlobalRef s (session); | GlobalRef s (session); | ||||
| MessageManager::callAsync ([s]() | MessageManager::callAsync ([s]() | ||||
| { | |||||
| getEnv()->CallVoidMethod (s, CameraCaptureSession.close); | |||||
| }); | |||||
| { | |||||
| getEnv()->CallVoidMethod (s, CameraCaptureSession.close); | |||||
| }); | |||||
| return; | return; | ||||
| } | } | ||||
| { | { | ||||
| const ScopedLock lock (captureSessionLock); | |||||
| captureSession = GlobalRef (session); | |||||
| const ScopedLock lock (t.captureSessionLock); | |||||
| t.captureSession = GlobalRef (session); | |||||
| } | } | ||||
| MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { this }] | |||||
| { | |||||
| if (weakRef == nullptr) | |||||
| return; | |||||
| MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { &t }] | |||||
| { | |||||
| if (weakRef == nullptr) | |||||
| return; | |||||
| weakRef->stillPictureTaker.reset (new StillPictureTaker (weakRef->captureSession, | |||||
| weakRef->captureRequestBuilder, | |||||
| weakRef->previewCaptureRequest, | |||||
| weakRef->handler, | |||||
| weakRef->autoFocusMode)); | |||||
| weakRef->stillPictureTaker.reset (new StillPictureTaker (weakRef->captureSession, | |||||
| weakRef->captureRequestBuilder, | |||||
| weakRef->previewCaptureRequest, | |||||
| weakRef->handler, | |||||
| weakRef->autoFocusMode)); | |||||
| weakRef->configuredCallback.captureSessionConfigured (weakRef.get()); | |||||
| }); | |||||
| weakRef->configuredCallback.captureSessionConfigured (weakRef.get()); | |||||
| }); | |||||
| } | } | ||||
| void cameraCaptureSessionReady ([[maybe_unused]] const LocalRef<jobject>& session) | |||||
| static void cameraCaptureSessionReadyCallback ([[maybe_unused]] JNIEnv* env, | |||||
| [[maybe_unused]] CaptureSession& t, | |||||
| [[maybe_unused]] jobject rawSession) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraCaptureSessionReady()"); | JUCE_CAMERA_LOG ("cameraCaptureSessionReady()"); | ||||
| } | } | ||||
| //============================================================================== | |||||
| static void cameraCaptureSessionActiveCallback (JNIEnv*, jobject, jlong host, jobject rawSession) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<CaptureSession*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| myself->cameraCaptureSessionActive (session); | |||||
| } | |||||
| } | |||||
| static void cameraCaptureSessionClosedCallback (JNIEnv*, jobject, jlong host, jobject rawSession) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<CaptureSession*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| myself->cameraCaptureSessionClosed (session); | |||||
| } | |||||
| } | |||||
| static void cameraCaptureSessionConfigureFailedCallback (JNIEnv*, jobject, jlong host, jobject rawSession) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<CaptureSession*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| myself->cameraCaptureSessionConfigureFailed (session); | |||||
| } | |||||
| } | |||||
| static void cameraCaptureSessionConfiguredCallback (JNIEnv*, jobject, jlong host, jobject rawSession) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<CaptureSession*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| myself->cameraCaptureSessionConfigured (session); | |||||
| } | |||||
| } | |||||
| static void cameraCaptureSessionReadyCallback (JNIEnv*, jobject, jlong host, jobject rawSession) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<CaptureSession*> (host)) | |||||
| { | |||||
| LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); | |||||
| myself->cameraCaptureSessionReady (session); | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| friend class ScopedCameraDevice; | friend class ScopedCameraDevice; | ||||
| @@ -2374,10 +2314,10 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| CALLBACK (cameraDeviceStateClosedCallback, "cameraDeviceStateClosed", "(JLandroid/hardware/camera2/CameraDevice;)V") \ | |||||
| CALLBACK (cameraDeviceStateDisconnectedCallback, "cameraDeviceStateDisconnected", "(JLandroid/hardware/camera2/CameraDevice;)V") \ | |||||
| CALLBACK (cameraDeviceStateErrorCallback, "cameraDeviceStateError", "(JLandroid/hardware/camera2/CameraDevice;I)V") \ | |||||
| CALLBACK (cameraDeviceStateOpenedCallback, "cameraDeviceStateOpened", "(JLandroid/hardware/camera2/CameraDevice;)V") | |||||
| CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateClosedCallback>, "cameraDeviceStateClosed", "(JLandroid/hardware/camera2/CameraDevice;)V") \ | |||||
| CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateDisconnectedCallback>, "cameraDeviceStateDisconnected", "(JLandroid/hardware/camera2/CameraDevice;)V") \ | |||||
| CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateErrorCallback>, "cameraDeviceStateError", "(JLandroid/hardware/camera2/CameraDevice;I)V") \ | |||||
| CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateOpenedCallback>, "cameraDeviceStateOpened", "(JLandroid/hardware/camera2/CameraDevice;)V") | |||||
| DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraDeviceStateCallback, "com/rmsl/juce/CameraDeviceStateCallback", 21) | DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraDeviceStateCallback, "com/rmsl/juce/CameraDeviceStateCallback", 21) | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| @@ -2390,92 +2330,66 @@ private: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void cameraDeviceStateClosed() | |||||
| void notifyOpenResult() | |||||
| { | |||||
| MessageManager::callAsync ([this]() { owner.cameraOpenFinished (openError); }); | |||||
| } | |||||
| //============================================================================== | |||||
| static void cameraDeviceStateClosedCallback (JNIEnv*, ScopedCameraDevice& s, jobject) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraDeviceStateClosed()"); | JUCE_CAMERA_LOG ("cameraDeviceStateClosed()"); | ||||
| closedEvent.signal(); | |||||
| s.closedEvent.signal(); | |||||
| } | } | ||||
| void cameraDeviceStateDisconnected() | |||||
| static void cameraDeviceStateDisconnectedCallback (JNIEnv*, ScopedCameraDevice& s, jobject) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraDeviceStateDisconnected()"); | JUCE_CAMERA_LOG ("cameraDeviceStateDisconnected()"); | ||||
| if (pendingOpen.compareAndSetBool (0, 1)) | |||||
| if (s.pendingOpen.compareAndSetBool (0, 1)) | |||||
| { | { | ||||
| openError = "Device disconnected"; | |||||
| s.openError = "Device disconnected"; | |||||
| notifyOpenResult(); | |||||
| s.notifyOpenResult(); | |||||
| } | } | ||||
| MessageManager::callAsync ([this]() { close(); }); | |||||
| MessageManager::callAsync ([&s] { s.close(); }); | |||||
| } | } | ||||
| void cameraDeviceStateError (int errorCode) | |||||
| static void cameraDeviceStateErrorCallback (JNIEnv*, ScopedCameraDevice& s, jobject, jint errorCode) | |||||
| { | { | ||||
| String error = cameraErrorCodeToString (errorCode); | |||||
| auto error = cameraErrorCodeToString (errorCode); | |||||
| JUCE_CAMERA_LOG ("cameraDeviceStateError(), error: " + error); | JUCE_CAMERA_LOG ("cameraDeviceStateError(), error: " + error); | ||||
| if (pendingOpen.compareAndSetBool (0, 1)) | |||||
| if (s.pendingOpen.compareAndSetBool (0, 1)) | |||||
| { | { | ||||
| openError = error; | |||||
| s.openError = error; | |||||
| notifyOpenResult(); | |||||
| s.notifyOpenResult(); | |||||
| } | } | ||||
| fatalErrorOccurred.set (1); | |||||
| s.fatalErrorOccurred.set (1); | |||||
| MessageManager::callAsync ([this, error]() | |||||
| MessageManager::callAsync ([&s, error]() | |||||
| { | { | ||||
| owner.cameraDeviceError (error); | |||||
| close(); | |||||
| s.owner.cameraDeviceError (error); | |||||
| s.close(); | |||||
| }); | }); | ||||
| } | } | ||||
| void cameraDeviceStateOpened (const LocalRef<jobject>& cameraDeviceToUse) | |||||
| static void cameraDeviceStateOpenedCallback (JNIEnv* env, ScopedCameraDevice& s, jobject cameraDeviceToUse) | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("cameraDeviceStateOpened()"); | JUCE_CAMERA_LOG ("cameraDeviceStateOpened()"); | ||||
| pendingOpen.set (0); | |||||
| LocalRef<jobject> camera (env->NewLocalRef (cameraDeviceToUse)); | |||||
| cameraDevice = GlobalRef (cameraDeviceToUse); | |||||
| s.pendingOpen.set (0); | |||||
| notifyOpenResult(); | |||||
| } | |||||
| s.cameraDevice = GlobalRef (camera); | |||||
| void notifyOpenResult() | |||||
| { | |||||
| MessageManager::callAsync ([this]() { owner.cameraOpenFinished (openError); }); | |||||
| } | |||||
| //============================================================================== | |||||
| static void JNICALL cameraDeviceStateClosedCallback (JNIEnv*, jobject, jlong host, jobject) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) | |||||
| myself->cameraDeviceStateClosed(); | |||||
| } | |||||
| static void JNICALL cameraDeviceStateDisconnectedCallback (JNIEnv*, jobject, jlong host, jobject) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) | |||||
| myself->cameraDeviceStateDisconnected(); | |||||
| } | |||||
| static void JNICALL cameraDeviceStateErrorCallback (JNIEnv*, jobject, jlong host, jobject, jint error) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) | |||||
| myself->cameraDeviceStateError (error); | |||||
| } | |||||
| static void JNICALL cameraDeviceStateOpenedCallback (JNIEnv*, jobject, jlong host, jobject rawCamera) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) | |||||
| { | |||||
| LocalRef<jobject> camera(getEnv()->NewLocalRef(rawCamera)); | |||||
| myself->cameraDeviceStateOpened (camera); | |||||
| } | |||||
| s.notifyOpenResult(); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -2815,7 +2729,7 @@ private: | |||||
| METHOD (constructor, "<init>", "(JLandroid/content/Context;I)V") \ | METHOD (constructor, "<init>", "(JLandroid/content/Context;I)V") \ | ||||
| METHOD (disable, "disable", "()V") \ | METHOD (disable, "disable", "()V") \ | ||||
| METHOD (enable, "enable", "()V") \ | METHOD (enable, "enable", "()V") \ | ||||
| CALLBACK (deviceOrientationChanged, "deviceOrientationChanged", "(JI)V") | |||||
| CALLBACK (generatedCallback<&DeviceOrientationChangeListener::orientationChanged>, "deviceOrientationChanged", "(JI)V") | |||||
| DECLARE_JNI_CLASS_WITH_MIN_SDK (OrientationEventListener, "com/rmsl/juce/JuceOrientationEventListener", 21) | DECLARE_JNI_CLASS_WITH_MIN_SDK (OrientationEventListener, "com/rmsl/juce/JuceOrientationEventListener", 21) | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| @@ -2830,7 +2744,7 @@ private: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void orientationChanged (int orientation) | |||||
| static void orientationChanged (JNIEnv*, DeviceOrientationChangeListener& t, jint orientation) | |||||
| { | { | ||||
| jassert (orientation < 360); | jassert (orientation < 360); | ||||
| @@ -2838,25 +2752,31 @@ private: | |||||
| if (orientation < 0) | if (orientation < 0) | ||||
| return; | return; | ||||
| auto oldOrientation = deviceOrientation; | |||||
| const auto oldOrientation = t.deviceOrientation; | |||||
| t.deviceOrientation = [orientation] | |||||
| { | |||||
| if (orientation > (360 - 45) || orientation < 45) | |||||
| return Desktop::upright; | |||||
| if (orientation < 135) | |||||
| return Desktop::rotatedClockwise; | |||||
| if (orientation < 225) | |||||
| return Desktop::upsideDown; | |||||
| return Desktop::rotatedAntiClockwise; | |||||
| }(); | |||||
| // NB: this assumes natural position to be portrait always, but some devices may be landscape... | // NB: this assumes natural position to be portrait always, but some devices may be landscape... | ||||
| if (orientation > (360 - 45) || orientation < 45) | |||||
| deviceOrientation = Desktop::upright; | |||||
| else if (orientation < 135) | |||||
| deviceOrientation = Desktop::rotatedClockwise; | |||||
| else if (orientation < 225) | |||||
| deviceOrientation = Desktop::upsideDown; | |||||
| else | |||||
| deviceOrientation = Desktop::rotatedAntiClockwise; | |||||
| if (oldOrientation != deviceOrientation) | |||||
| if (oldOrientation != t.deviceOrientation) | |||||
| { | { | ||||
| lastKnownScreenOrientation = Desktop::getInstance().getCurrentOrientation(); | |||||
| t.lastKnownScreenOrientation = Desktop::getInstance().getCurrentOrientation(); | |||||
| // Need to update preview transform, but screen orientation will change slightly | // Need to update preview transform, but screen orientation will change slightly | ||||
| // later than sensor orientation. | // later than sensor orientation. | ||||
| startTimer (500); | |||||
| t.startTimer (500); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2881,12 +2801,6 @@ private: | |||||
| numChecksForOrientationChange = 10; | numChecksForOrientationChange = 10; | ||||
| } | } | ||||
| } | } | ||||
| static void deviceOrientationChanged (JNIEnv*, jobject /*obj*/, jlong host, jint orientation) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<DeviceOrientationChangeListener*> (host)) | |||||
| myself->orientationChanged (orientation); | |||||
| } | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -3180,7 +3094,7 @@ private: | |||||
| { | { | ||||
| auto* env = getEnv(); | auto* env = getEnv(); | ||||
| auto quitSafelyMethod = env->GetMethodID(AndroidHandlerThread, "quitSafely", "()Z"); | |||||
| auto quitSafelyMethod = env->GetMethodID (AndroidHandlerThread, "quitSafely", "()Z"); | |||||
| // this code will only run on SDK >= 21 | // this code will only run on SDK >= 21 | ||||
| jassert(quitSafelyMethod != nullptr); | jassert(quitSafelyMethod != nullptr); | ||||
| @@ -3268,9 +3182,3 @@ String CameraDevice::getFileExtension() | |||||
| { | { | ||||
| return ".mp4"; | return ".mp4"; | ||||
| } | } | ||||
| //============================================================================== | |||||
| CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::StillPictureTaker::CameraCaptureSessionCaptureCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::StillPictureTaker::CameraCaptureSessionCaptureCallback; | |||||
| CameraDevice::Pimpl::ScopedCameraDevice::CameraDeviceStateCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CameraDeviceStateCallback; | |||||
| CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::CameraCaptureSessionStateCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::CameraCaptureSessionStateCallback; | |||||
| CameraDevice::Pimpl::DeviceOrientationChangeListener::OrientationEventListener_Class CameraDevice::Pimpl::DeviceOrientationChangeListener::OrientationEventListener; | |||||
| @@ -32,7 +32,7 @@ | |||||
| // | // | ||||
| // files with min sdk version 21 | // files with min sdk version 21 | ||||
| // See juce_core/native/java/README.txt on how to generate this byte-code. | // See juce_core/native/java/README.txt on how to generate this byte-code. | ||||
| static const unsigned char MediaSessionByteCode[] = | |||||
| static const uint8 MediaSessionByteCode[] = | |||||
| { 31,139,8,8,247,108,161,94,0,3,77,101,100,105,97,83,101,115,115,105,111,110,66,121,116,101,67,111,100,101,46,100,101,120,0,149, | { 31,139,8,8,247,108,161,94,0,3,77,101,100,105,97,83,101,115,115,105,111,110,66,121,116,101,67,111,100,101,46,100,101,120,0,149, | ||||
| 152,127,108,28,71,21,199,223,236,253,180,207,190,95,254,221,186,169,211,56,137,19,234,220,145,26,226,228,28,99,199,216,196,233, | 152,127,108,28,71,21,199,223,236,253,180,207,190,95,254,221,186,169,211,56,137,19,234,220,145,26,226,228,28,99,199,216,196,233, | ||||
| 249,71,125,182,107,76,168,187,246,109,236,77,238,118,143,221,189,171,45,132,168,170,32,21,209,63,144,74,165,170,82,81,144,64, | 249,71,125,182,107,76,168,187,246,109,236,77,238,118,143,221,189,171,45,132,168,170,32,21,209,63,144,74,165,170,82,81,144,64, | ||||
| @@ -757,12 +757,12 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| CALLBACK (audioInfoChanged, "mediaControllerAudioInfoChanged", "(JLandroid/media/session/MediaController$PlaybackInfo;)V") \ | |||||
| CALLBACK (metadataChanged, "mediaControllerMetadataChanged", "(JLandroid/media/MediaMetadata;)V") \ | |||||
| CALLBACK (playbackStateChanged, "mediaControllerPlaybackStateChanged", "(JLandroid/media/session/PlaybackState;)V") \ | |||||
| CALLBACK (sessionDestroyed, "mediaControllerSessionDestroyed", "(J)V") | |||||
| CALLBACK (generatedCallback<&Controller::audioInfoChanged>, "mediaControllerAudioInfoChanged", "(JLandroid/media/session/MediaController$PlaybackInfo;)V") \ | |||||
| CALLBACK (generatedCallback<&Controller::metadataChanged>, "mediaControllerMetadataChanged", "(JLandroid/media/MediaMetadata;)V") \ | |||||
| CALLBACK (generatedCallback<&Controller::playbackStateChanged>, "mediaControllerPlaybackStateChanged", "(JLandroid/media/session/PlaybackState;)V") \ | |||||
| CALLBACK (generatedCallback<&Controller::sessionDestroyed>, "mediaControllerSessionDestroyed", "(J)V") | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (AndroidMediaControllerCallback, "com/rmsl/juce/MediaControllerCallback", 21, MediaSessionByteCode, sizeof (MediaSessionByteCode)) | |||||
| DECLARE_JNI_CLASS_WITH_BYTECODE (AndroidMediaControllerCallback, "com/rmsl/juce/MediaControllerCallback", 21, MediaSessionByteCode) | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| LocalRef<jobject> createControllerCallbacks() | LocalRef<jobject> createControllerCallbacks() | ||||
| @@ -774,32 +774,24 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| // MediaSessionController callbacks | // MediaSessionController callbacks | ||||
| static void audioInfoChanged (JNIEnv*, jobject, jlong host, [[maybe_unused]] jobject playbackInfo) | |||||
| static void audioInfoChanged (JNIEnv*, [[maybe_unused]] Controller& t, [[maybe_unused]] jobject playbackInfo) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::audioInfoChanged()"); | |||||
| } | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::audioInfoChanged()"); | |||||
| } | } | ||||
| static void metadataChanged (JNIEnv*, jobject, jlong host, [[maybe_unused]] jobject metadata) | |||||
| static void metadataChanged (JNIEnv*, [[maybe_unused]] Controller&, [[maybe_unused]] jobject metadata) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::metadataChanged()"); | |||||
| } | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::metadataChanged()"); | |||||
| } | } | ||||
| static void playbackStateChanged (JNIEnv*, jobject, jlong host, jobject state) | |||||
| static void playbackStateChanged (JNIEnv*, Controller& t, [[maybe_unused]] jobject state) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) | |||||
| myself->stateChanged (state); | |||||
| t.stateChanged (state); | |||||
| } | } | ||||
| static void sessionDestroyed (JNIEnv*, jobject, jlong host) | |||||
| static void sessionDestroyed (JNIEnv*, [[maybe_unused]] Controller& t) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::sessionDestroyed()"); | |||||
| JUCE_VIDEO_LOG ("MediaSessionController::sessionDestroyed()"); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -1279,11 +1271,11 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(J)V") \ | METHOD (constructor, "<init>", "(J)V") \ | ||||
| CALLBACK (pauseCallback, "mediaSessionPause", "(J)V") \ | |||||
| CALLBACK (playCallback, "mediaSessionPlay", "(J)V") \ | |||||
| CALLBACK (playFromMediaIdCallback, "mediaSessionPlayFromMediaId", "(JLjava/lang/String;Landroid/os/Bundle;)V") \ | |||||
| CALLBACK (seekToCallback, "mediaSessionSeekTo", "(JJ)V") \ | |||||
| CALLBACK (stopCallback, "mediaSessionStop", "(J)V") | |||||
| CALLBACK (generatedCallback<&MediaSession::pauseCallback>, "mediaSessionPause", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&MediaSession::playCallback>, "mediaSessionPlay", "(J)V") \ | |||||
| CALLBACK (generatedCallback<&MediaSession::playFromMediaIdCallback>, "mediaSessionPlayFromMediaId", "(JLjava/lang/String;Landroid/os/Bundle;)V") \ | |||||
| CALLBACK (generatedCallback<&MediaSession::seekToCallback>, "mediaSessionSeekTo", "(JJ)V") \ | |||||
| CALLBACK (generatedCallback<&MediaSession::stopCallback>, "mediaSessionStop", "(J)V") | |||||
| DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSessionCallback, "com/rmsl/juce/MediaSessionCallback", 21) | DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSessionCallback, "com/rmsl/juce/MediaSessionCallback", 21) | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| @@ -1297,78 +1289,62 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| // MediaSession callbacks | // MediaSession callbacks | ||||
| static void pauseCallback (JNIEnv*, jobject, jlong host) | |||||
| static void pauseCallback (JNIEnv*, MediaSession& t) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSession::pauseCallback()"); | |||||
| myself->player.pause(); | |||||
| myself->updatePlaybackState(); | |||||
| myself->abandonAudioFocus(); | |||||
| } | |||||
| JUCE_VIDEO_LOG ("MediaSession::pauseCallback()"); | |||||
| t.player.pause(); | |||||
| t.updatePlaybackState(); | |||||
| t.abandonAudioFocus(); | |||||
| } | } | ||||
| static void playCallback (JNIEnv*, jobject, jlong host) | |||||
| static void playCallback (JNIEnv* env, MediaSession& t) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSession::playCallback()"); | |||||
| JUCE_VIDEO_LOG ("MediaSession::playCallback()"); | |||||
| myself->requestAudioFocus(); | |||||
| t.requestAudioFocus(); | |||||
| if (! myself->hasAudioFocus) | |||||
| { | |||||
| myself->errorOccurred ("Application has been denied audio focus. Try again later."); | |||||
| return; | |||||
| } | |||||
| if (! t.hasAudioFocus) | |||||
| { | |||||
| t.errorOccurred ("Application has been denied audio focus. Try again later."); | |||||
| return; | |||||
| } | |||||
| getEnv()->CallVoidMethod (myself->nativeMediaSession, AndroidMediaSession.setActive, true); | |||||
| env->CallVoidMethod (t.nativeMediaSession, AndroidMediaSession.setActive, true); | |||||
| myself->player.play(); | |||||
| myself->setSpeed (myself->playSpeedMult); | |||||
| myself->updatePlaybackState(); | |||||
| } | |||||
| t.player.play(); | |||||
| t.setSpeed (t.playSpeedMult); | |||||
| t.updatePlaybackState(); | |||||
| } | } | ||||
| static void playFromMediaIdCallback (JNIEnv* env, jobject, jlong host, jstring mediaId, jobject extras) | |||||
| static void playFromMediaIdCallback (JNIEnv* env, MediaSession& t, jstring mediaId, jobject extras) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSession::playFromMediaIdCallback()"); | |||||
| JUCE_VIDEO_LOG ("MediaSession::playFromMediaIdCallback()"); | |||||
| myself->player.load (LocalRef<jstring> ((jstring) env->NewLocalRef(mediaId)), LocalRef<jobject> (env->NewLocalRef(extras))); | |||||
| myself->updatePlaybackState(); | |||||
| } | |||||
| t.player.load (LocalRef<jstring> ((jstring) env->NewLocalRef (mediaId)), LocalRef<jobject> (env->NewLocalRef (extras))); | |||||
| t.updatePlaybackState(); | |||||
| } | } | ||||
| static void seekToCallback (JNIEnv* /*env*/, jobject, jlong host, jlong pos) | |||||
| static void seekToCallback (JNIEnv*, MediaSession& t, jlong pos) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSession::seekToCallback()"); | |||||
| JUCE_VIDEO_LOG ("MediaSession::seekToCallback()"); | |||||
| myself->pendingSeekRequest = true; | |||||
| myself->player.setPlayPosition ((jint) pos); | |||||
| myself->updatePlaybackState(); | |||||
| } | |||||
| t.pendingSeekRequest = true; | |||||
| t.player.setPlayPosition ((jint) pos); | |||||
| t.updatePlaybackState(); | |||||
| } | } | ||||
| static void stopCallback(JNIEnv* env, jobject, jlong host) | |||||
| static void stopCallback (JNIEnv* env, MediaSession& t) | |||||
| { | { | ||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) | |||||
| { | |||||
| JUCE_VIDEO_LOG ("MediaSession::stopCallback()"); | |||||
| JUCE_VIDEO_LOG ("MediaSession::stopCallback()"); | |||||
| env->CallVoidMethod (myself->nativeMediaSession, AndroidMediaSession.setActive, false); | |||||
| env->CallVoidMethod (t.nativeMediaSession, AndroidMediaSession.setActive, false); | |||||
| myself->player.closeVideo(); | |||||
| myself->updatePlaybackState(); | |||||
| t.player.closeVideo(); | |||||
| t.updatePlaybackState(); | |||||
| myself->abandonAudioFocus(); | |||||
| t.abandonAudioFocus(); | |||||
| myself->owner.closeVideoFinished(); | |||||
| } | |||||
| t.owner.closeVideoFinished(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1673,7 +1649,7 @@ private: | |||||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | ||||
| METHOD (constructor, "<init>", "(Landroid/app/Activity;J)V") \ | METHOD (constructor, "<init>", "(Landroid/app/Activity;J)V") \ | ||||
| METHOD (setEnabled, "setEnabled", "(Z)V") \ | METHOD (setEnabled, "setEnabled", "(Z)V") \ | ||||
| CALLBACK (systemVolumeChangedCallback, "mediaSessionSystemVolumeChanged", "(J)V") | |||||
| CALLBACK (generatedCallback<&SystemVolumeListener::systemVolumeChanged>, "mediaSessionSystemVolumeChanged", "(J)V") | |||||
| DECLARE_JNI_CLASS_WITH_MIN_SDK (SystemVolumeObserver, "com/rmsl/juce/SystemVolumeObserver", 21) | DECLARE_JNI_CLASS_WITH_MIN_SDK (SystemVolumeObserver, "com/rmsl/juce/SystemVolumeObserver", 21) | ||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| @@ -1693,14 +1669,14 @@ private: | |||||
| // Send first notification instantly to ensure sync. | // Send first notification instantly to ensure sync. | ||||
| if (shouldBeEnabled) | if (shouldBeEnabled) | ||||
| systemVolumeChanged(); | |||||
| systemVolumeChanged (getEnv(), *this); | |||||
| } | } | ||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| void systemVolumeChanged() | |||||
| static void systemVolumeChanged (JNIEnv*, SystemVolumeListener& t) | |||||
| { | { | ||||
| MessageManager::callAsync ([weakThis = WeakReference<SystemVolumeListener> { this }]() mutable | |||||
| MessageManager::callAsync ([weakThis = WeakReference<SystemVolumeListener> { &t }] | |||||
| { | { | ||||
| if (weakThis == nullptr) | if (weakThis == nullptr) | ||||
| return; | return; | ||||
| @@ -1711,13 +1687,6 @@ private: | |||||
| } | } | ||||
| //============================================================================== | |||||
| static void systemVolumeChangedCallback (JNIEnv*, jobject, jlong host) | |||||
| { | |||||
| if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::SystemVolumeListener*> (host)) | |||||
| myself->systemVolumeChanged(); | |||||
| } | |||||
| JUCE_DECLARE_WEAK_REFERENCEABLE (SystemVolumeListener) | JUCE_DECLARE_WEAK_REFERENCEABLE (SystemVolumeListener) | ||||
| }; | }; | ||||
| @@ -1829,8 +1798,3 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| constexpr VideoComponent::Pimpl::MediaSession::Player::StateInfo VideoComponent::Pimpl::MediaSession::Player::stateInfos[]; | constexpr VideoComponent::Pimpl::MediaSession::Player::StateInfo VideoComponent::Pimpl::MediaSession::Player::stateInfos[]; | ||||
| //============================================================================== | |||||
| VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback_Class VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback; | |||||
| VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback_Class VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback; | |||||
| VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver_Class VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver; | |||||