| @@ -327,7 +327,7 @@ static const uint8 javaMidiByteCode[] = | |||
| STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ | |||
| 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 | |||
| #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> 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 | |||
| @@ -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_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 | |||
| #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) | |||
| { | |||
| SystemJavaClassComparator comparator; | |||
| @@ -552,12 +552,12 @@ static const uint8 javaFragmentOverlay[] = | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (construct, "<init>", "()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 | |||
| #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()); | |||
| } | |||
| 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() | |||
| @@ -172,7 +172,7 @@ struct SystemJavaClassComparator; | |||
| class JNIClassBase | |||
| { | |||
| 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(); | |||
| 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_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); | |||
| #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_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) \ | |||
| 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) \ | |||
| @@ -1005,20 +1008,20 @@ public: | |||
| const Array<int>& /*grantResults*/) {} | |||
| 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: | |||
| jobject getNativeHandle(); | |||
| private: | |||
| 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); | |||
| 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 | |||
| @@ -195,7 +195,7 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer") | |||
| METHOD (isExhausted, "isExhausted", "()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 | |||
| //============================================================================== | |||
| @@ -342,15 +342,6 @@ namespace detail | |||
| { | |||
| 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 | |||
| //============================================================================== | |||
| @@ -396,7 +387,7 @@ struct ObjCClass | |||
| } | |||
| 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> | |||
| void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) | |||
| @@ -336,6 +336,28 @@ namespace juce | |||
| #include "native/juce_linux_FileChooser.cpp" | |||
| #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 "native/accessibility/juce_android_Accessibility.cpp" | |||
| #include "native/juce_android_Windowing.cpp" | |||
| @@ -26,7 +26,6 @@ | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** | |||
| An abstract base class which can be implemented by components that function as | |||
| text editors. | |||
| @@ -297,21 +297,13 @@ public: | |||
| { | |||
| 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(); | |||
| @@ -32,9 +32,19 @@ import android.graphics.Canvas; | |||
| import android.graphics.ColorMatrix; | |||
| import android.graphics.ColorMatrixColorFilter; | |||
| import android.graphics.Paint; | |||
| import android.graphics.Point; | |||
| 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.text.Editable; | |||
| import android.text.InputType; | |||
| import android.text.SpannableStringBuilder; | |||
| import android.view.Choreographer; | |||
| import android.view.KeyEvent; | |||
| import android.view.MotionEvent; | |||
| @@ -51,6 +61,7 @@ import android.view.inputmethod.InputMethodManager; | |||
| import java.lang.reflect.Method; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| public final class ComponentPeerView extends ViewGroup | |||
| @@ -63,9 +74,10 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (Application.class.isInstance (context)) | |||
| { | |||
| ((Application) context).registerActivityLifecycleCallbacks (this); | |||
| } else | |||
| } | |||
| else | |||
| { | |||
| ((Application) context.getApplicationContext ()).registerActivityLifecycleCallbacks (this); | |||
| ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks (this); | |||
| } | |||
| this.host = host; | |||
| @@ -77,7 +89,7 @@ public final class ComponentPeerView extends ViewGroup | |||
| setOnFocusChangeListener (this); | |||
| // 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, | |||
| 0, 1.0f, 0, 0, 0, | |||
| @@ -91,10 +103,12 @@ public final class ComponentPeerView extends ViewGroup | |||
| 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; | |||
| 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); | |||
| } | |||
| public void clear () | |||
| public void clear() | |||
| { | |||
| host = 0; | |||
| } | |||
| @@ -147,14 +164,14 @@ public final class ComponentPeerView extends ViewGroup | |||
| } | |||
| @Override | |||
| public boolean isOpaque () | |||
| public boolean isOpaque() | |||
| { | |||
| return opaque; | |||
| } | |||
| private final boolean opaque; | |||
| 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); | |||
| @@ -168,25 +185,25 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (host == 0) | |||
| return false; | |||
| int action = event.getAction (); | |||
| long time = event.getEventTime (); | |||
| int action = event.getAction(); | |||
| long time = event.getEventTime(); | |||
| switch (action & MotionEvent.ACTION_MASK) | |||
| { | |||
| 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; | |||
| case MotionEvent.ACTION_CANCEL: | |||
| 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; | |||
| 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) | |||
| { | |||
| @@ -206,8 +223,9 @@ public final class ComponentPeerView extends ViewGroup | |||
| 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]; | |||
| getLocationOnScreen (point); | |||
| @@ -223,8 +241,9 @@ public final class ComponentPeerView extends ViewGroup | |||
| 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]; | |||
| getLocationOnScreen (point); | |||
| @@ -253,32 +272,132 @@ public final class ComponentPeerView extends ViewGroup | |||
| 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 handleKeyUp (long host, int keycode, int textchar); | |||
| private native void handleBackButton (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) | |||
| return; | |||
| @@ -292,6 +411,11 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (host == 0) | |||
| 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) | |||
| { | |||
| case KeyEvent.KEYCODE_VOLUME_UP: | |||
| @@ -299,7 +423,7 @@ public final class ComponentPeerView extends ViewGroup | |||
| return super.onKeyDown (keyCode, event); | |||
| case KeyEvent.KEYCODE_BACK: | |||
| { | |||
| backButtonPressed (); | |||
| backButtonPressed(); | |||
| return true; | |||
| } | |||
| @@ -309,8 +433,9 @@ public final class ComponentPeerView extends ViewGroup | |||
| handleKeyDown (host, | |||
| keyCode, | |||
| event.getUnicodeChar (), | |||
| event.getMetaState ()); | |||
| event.getUnicodeChar(), | |||
| event.getMetaState()); | |||
| return true; | |||
| } | |||
| @@ -320,7 +445,7 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (host == 0) | |||
| return false; | |||
| handleKeyUp (host, keyCode, event.getUnicodeChar ()); | |||
| handleKeyUp (host, keyCode, event.getUnicodeChar()); | |||
| return true; | |||
| } | |||
| @@ -330,17 +455,17 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (host == 0) | |||
| 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); | |||
| 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, | |||
| keyCode, | |||
| utf8Char, | |||
| event.getMetaState ()); | |||
| event.getMetaState()); | |||
| return true; | |||
| } | |||
| @@ -355,39 +480,40 @@ public final class ComponentPeerView extends ViewGroup | |||
| 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 | |||
| { | |||
| TreeObserver () | |||
| TreeObserver() | |||
| { | |||
| keyboardShown = false; | |||
| } | |||
| @Override | |||
| public void onGlobalLayout () | |||
| public void onGlobalLayout() | |||
| { | |||
| Rect r = new Rect (); | |||
| Rect r = new Rect(); | |||
| View parentView = getRootView (); | |||
| View parentView = getRootView(); | |||
| int diff; | |||
| if (parentView == null) | |||
| { | |||
| getWindowVisibleDisplayFrame (r); | |||
| diff = getHeight () - (r.bottom - r.top); | |||
| } else | |||
| diff = getHeight() - (r.bottom - r.top); | |||
| } | |||
| else | |||
| { | |||
| 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. | |||
| @@ -397,7 +523,7 @@ public final class ComponentPeerView extends ViewGroup | |||
| handleKeyboardHidden (view.host); | |||
| } | |||
| if (!keyboardShown && diff > 20) | |||
| if (! keyboardShown && diff > 20) | |||
| keyboardShown = true; | |||
| } | |||
| @@ -405,26 +531,219 @@ public final class ComponentPeerView extends ViewGroup | |||
| } | |||
| private final ComponentPeerView view; | |||
| private final TreeObserver viewTreeObserver = new TreeObserver (); | |||
| private final TreeObserver viewTreeObserver = new TreeObserver(); | |||
| } | |||
| 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 | |||
| public InputConnection onCreateInputConnection (EditorInfo outAttrs) | |||
| { | |||
| TextInputTarget focused = getFocusedTextInputTarget (host); | |||
| outAttrs.actionLabel = ""; | |||
| outAttrs.hintText = ""; | |||
| 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.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 | |||
| protected void onSizeChanged (int w, int h, int oldw, int oldh) | |||
| @@ -463,11 +782,13 @@ public final class ComponentPeerView extends ViewGroup | |||
| Method systemUIVisibilityMethod = null; | |||
| try | |||
| { | |||
| systemUIVisibilityMethod = this.getClass ().getMethod ("setSystemUiVisibility", int.class); | |||
| } catch (SecurityException e) | |||
| systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class); | |||
| } | |||
| catch (SecurityException e) | |||
| { | |||
| return; | |||
| } catch (NoSuchMethodException e) | |||
| } | |||
| catch (NoSuchMethodException e) | |||
| { | |||
| return; | |||
| } | |||
| @@ -476,18 +797,21 @@ public final class ComponentPeerView extends ViewGroup | |||
| try | |||
| { | |||
| 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) | |||
| @@ -572,11 +896,22 @@ public final class ComponentPeerView extends ViewGroup | |||
| if (host == 0) | |||
| 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)) | |||
| { | |||
| nodeInfo.recycle(); | |||
| if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) | |||
| nodeInfo.recycle(); | |||
| return null; | |||
| } | |||
| @@ -617,10 +952,10 @@ public final class ComponentPeerView extends ViewGroup | |||
| } | |||
| 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 | |||
| public AccessibilityNodeProvider getAccessibilityNodeProvider () | |||
| public AccessibilityNodeProvider getAccessibilityNodeProvider() | |||
| { | |||
| return nodeProvider; | |||
| } | |||
| @@ -34,7 +34,6 @@ public final class JuceContentProviderFileObserver extends FileObserver | |||
| public JuceContentProviderFileObserver (long hostToUse, String path, int mask) | |||
| { | |||
| super (path, mask); | |||
| host = hostToUse; | |||
| } | |||
| @@ -101,7 +101,7 @@ public: | |||
| class Owner | |||
| { | |||
| public: | |||
| virtual ~Owner() {} | |||
| virtual ~Owner() = default; | |||
| virtual void cursorClosed (const AndroidContentSharerCursor&) = 0; | |||
| }; | |||
| @@ -121,9 +121,9 @@ public: | |||
| 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) | |||
| @@ -141,20 +141,12 @@ private: | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (addRow, "addRow", "([Ljava/lang/Object;)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 | |||
| static void JNICALL contentSharerCursorClosed (JNIEnv*, jobject, jlong host) | |||
| { | |||
| if (auto* myself = reinterpret_cast<AndroidContentSharerCursor*> (host)) | |||
| myself->cursorClosed(); | |||
| } | |||
| }; | |||
| AndroidContentSharerCursor::JuceContentProviderCursor_Class AndroidContentSharerCursor::JuceContentProviderCursor; | |||
| //============================================================================== | |||
| class AndroidContentSharerFileObserver | |||
| { | |||
| @@ -228,20 +220,17 @@ private: | |||
| METHOD (constructor, "<init>", "(JLjava/lang/String;I)V") \ | |||
| METHOD (startWatching, "startWatching", "()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 | |||
| 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 | |||
| { | |||
| @@ -891,6 +880,4 @@ ContentSharer::Pimpl* ContentSharer::createPimpl() | |||
| return new ContentSharerNativeImpl (*this); | |||
| } | |||
| ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider; | |||
| } // namespace juce | |||
| @@ -948,6 +948,11 @@ TextEditor::TextEditor (const String& name, juce_wchar passwordChar) | |||
| TextEditor::~TextEditor() | |||
| { | |||
| giveAwayKeyboardFocus(); | |||
| if (auto* peer = getPeer()) | |||
| peer->refreshTextInputTarget(); | |||
| textValue.removeListener (textHolder); | |||
| textValue.referTo (Value()); | |||
| @@ -1240,7 +1245,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage) | |||
| textValue = newText; | |||
| auto oldCursorPos = caretPosition; | |||
| bool cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); | |||
| auto cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); | |||
| clearInternal (nullptr); | |||
| 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 | |||
| @@ -182,7 +182,6 @@ bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textChar | |||
| textCharacter)); | |||
| } | |||
| bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo) | |||
| { | |||
| bool keyWasUsed = false; | |||
| @@ -607,7 +606,7 @@ void ComponentPeer::forceDisplayUpdate() | |||
| Desktop::getInstance().displays->refresh(); | |||
| } | |||
| void ComponentPeer::globalFocusChanged (Component*) | |||
| void ComponentPeer::globalFocusChanged ([[maybe_unused]] Component* comp) | |||
| { | |||
| refreshTextInputTarget(); | |||
| } | |||
| @@ -476,6 +476,11 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons | |||
| CodeEditorComponent::~CodeEditorComponent() | |||
| { | |||
| giveAwayKeyboardFocus(); | |||
| if (auto* peer = getPeer()) | |||
| peer->refreshTextInputTarget(); | |||
| document.removeListener (pimpl.get()); | |||
| } | |||
| @@ -1549,8 +1549,6 @@ struct JuceFirebaseInstanceIdService | |||
| instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token))); | |||
| } | |||
| }; | |||
| JuceFirebaseInstanceIdService::InstanceIdService_Class JuceFirebaseInstanceIdService::InstanceIdService; | |||
| #endif | |||
| #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) | |||
| @@ -1592,8 +1590,6 @@ struct JuceFirebaseMessagingService | |||
| LocalRef<jstring> (static_cast<jstring> (error))); | |||
| } | |||
| }; | |||
| JuceFirebaseMessagingService::MessagingService_Class JuceFirebaseMessagingService::MessagingService; | |||
| #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 | |||
| // 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, | |||
| 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, | |||
| @@ -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 | |||
| // 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, | |||
| 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, | |||
| @@ -479,82 +479,62 @@ private: | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (constructor, "<init>", "(J)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 | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (constructor, "<init>", "(J)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 | |||
| 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) \ | |||
| 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") | |||
| #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")); | |||
| if (responseClass != nullptr) | |||
| @@ -565,14 +545,14 @@ private: | |||
| { | |||
| auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method)); | |||
| owner.pageLoadHadNetworkError (juceString (errorString)); | |||
| t.owner.pageLoadHadNetworkError (juceString (errorString)); | |||
| return; | |||
| } | |||
| } | |||
| // Should never get here! | |||
| jassertfalse; | |||
| owner.pageLoadHadNetworkError ({}); | |||
| t.owner.pageLoadHadNetworkError ({}); | |||
| } | |||
| //============================================================================== | |||
| @@ -596,9 +576,7 @@ WebBrowserComponent::WebBrowserComponent (const Options& options) | |||
| addAndMakeVisible (browser.get()); | |||
| } | |||
| WebBrowserComponent::~WebBrowserComponent() | |||
| { | |||
| } | |||
| WebBrowserComponent::~WebBrowserComponent() = default; | |||
| //============================================================================== | |||
| void WebBrowserComponent::goToURL (const String& url, | |||
| @@ -728,7 +706,4 @@ bool WebBrowserComponent::areOptionsSupported (const Options& options) | |||
| 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 | |||
| @@ -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 | |||
| { | |||
| @@ -266,40 +246,46 @@ public: | |||
| Component& component; | |||
| 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) | |||
| @@ -414,25 +400,6 @@ private: | |||
| EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY; | |||
| 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() | |||
| { | |||
| @@ -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, | |||
| 0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x9d, 0x5a, | |||
| @@ -720,42 +720,17 @@ private: | |||
| METHOD (queryPurchases, "queryPurchases", "()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, | |||
| "com/rmsl/juce/JuceBillingClient", | |||
| 16, | |||
| juceBillingClientCompiled, | |||
| numElementsInArray (juceBillingClientCompiled)) | |||
| juceBillingClientCompiled) | |||
| #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 | |||
| { | |||
| @@ -1095,7 +1070,4 @@ void juce_handleOnResume() | |||
| }); | |||
| } | |||
| InAppPurchases::Pimpl::JuceBillingClient_Class InAppPurchases::Pimpl::JuceBillingClient; | |||
| } // namespace juce | |||
| @@ -400,7 +400,7 @@ class MediaRecorderOnInfoListener : public AndroidInterfaceImplementer | |||
| public: | |||
| struct Owner | |||
| { | |||
| virtual ~Owner() {} | |||
| virtual ~Owner() = default; | |||
| 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) \ | |||
| 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 | |||
| 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 | |||
| { | |||
| 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) \ | |||
| 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) | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -2161,124 +2142,83 @@ private: | |||
| 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()"); | |||
| } | |||
| void cameraCaptureSessionClosed ([[maybe_unused]] jobject session) | |||
| static void cameraCaptureSessionClosedCallback ([[maybe_unused]] JNIEnv* env, | |||
| CaptureSession& t, | |||
| [[maybe_unused]] jobject rawSession) | |||
| { | |||
| 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()"); | |||
| 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()"); | |||
| if (pendingClose.get() == 1) | |||
| if (t.pendingClose.get() == 1) | |||
| { | |||
| // Already closing, bailout. | |||
| closedEvent.signal(); | |||
| t.closedEvent.signal(); | |||
| GlobalRef s (session); | |||
| MessageManager::callAsync ([s]() | |||
| { | |||
| getEnv()->CallVoidMethod (s, CameraCaptureSession.close); | |||
| }); | |||
| { | |||
| getEnv()->CallVoidMethod (s, CameraCaptureSession.close); | |||
| }); | |||
| 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()"); | |||
| } | |||
| //============================================================================== | |||
| 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; | |||
| @@ -2374,10 +2314,10 @@ private: | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| 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) | |||
| #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()"); | |||
| closedEvent.signal(); | |||
| s.closedEvent.signal(); | |||
| } | |||
| void cameraDeviceStateDisconnected() | |||
| static void cameraDeviceStateDisconnectedCallback (JNIEnv*, ScopedCameraDevice& s, jobject) | |||
| { | |||
| 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); | |||
| 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()"); | |||
| 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 (disable, "disable", "()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) | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -2830,7 +2744,7 @@ private: | |||
| } | |||
| //============================================================================== | |||
| void orientationChanged (int orientation) | |||
| static void orientationChanged (JNIEnv*, DeviceOrientationChangeListener& t, jint orientation) | |||
| { | |||
| jassert (orientation < 360); | |||
| @@ -2838,25 +2752,31 @@ private: | |||
| if (orientation < 0) | |||
| 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... | |||
| 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 | |||
| // later than sensor orientation. | |||
| startTimer (500); | |||
| t.startTimer (500); | |||
| } | |||
| } | |||
| @@ -2881,12 +2801,6 @@ private: | |||
| 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 quitSafelyMethod = env->GetMethodID(AndroidHandlerThread, "quitSafely", "()Z"); | |||
| auto quitSafelyMethod = env->GetMethodID (AndroidHandlerThread, "quitSafely", "()Z"); | |||
| // this code will only run on SDK >= 21 | |||
| jassert(quitSafelyMethod != nullptr); | |||
| @@ -3268,9 +3182,3 @@ String CameraDevice::getFileExtension() | |||
| { | |||
| 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 | |||
| // 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, | |||
| 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, | |||
| @@ -757,12 +757,12 @@ private: | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| 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 | |||
| LocalRef<jobject> createControllerCallbacks() | |||
| @@ -774,32 +774,24 @@ private: | |||
| //============================================================================== | |||
| // 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) \ | |||
| 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) | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -1297,78 +1289,62 @@ private: | |||
| //============================================================================== | |||
| // 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) \ | |||
| METHOD (constructor, "<init>", "(Landroid/app/Activity;J)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) | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -1693,14 +1669,14 @@ private: | |||
| // Send first notification instantly to ensure sync. | |||
| if (shouldBeEnabled) | |||
| systemVolumeChanged(); | |||
| systemVolumeChanged (getEnv(), *this); | |||
| } | |||
| 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) | |||
| 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) | |||
| }; | |||
| @@ -1829,8 +1798,3 @@ private: | |||
| //============================================================================== | |||
| 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; | |||