1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-26 02:14:22 +00:00

TextInputTarget: Improve IME support on Android

This commit is contained in:
reuk 2022-11-29 13:15:47 +00:00
parent 5ed4b19e4d
commit da38c1ed2a
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
22 changed files with 1796 additions and 928 deletions

View file

@ -327,7 +327,7 @@ static const uint8 javaMidiByteCode[] =
STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;") STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \

View file

@ -113,4 +113,19 @@ Object withMember (Object copy, Member OtherObject::* member, Other&& value)
template <typename Fn> struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; template <typename Fn> struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } };
template <typename Fn> ScopeGuard (Fn) -> ScopeGuard<Fn>; template <typename Fn> ScopeGuard (Fn) -> ScopeGuard<Fn>;
#ifndef DOXYGEN
namespace detail
{
template <typename Functor, typename Return, typename... Args>
static constexpr auto toFnPtr (Functor functor, Return (Functor::*) (Args...) const)
{
return static_cast<Return (*) (Args...)> (functor);
}
} // namespace detail
#endif
/** Converts a captureless lambda to its equivalent function pointer type. */
template <typename Functor>
static constexpr auto toFnPtr (Functor functor) { return detail::toFnPtr (functor, &Functor::operator()); }
} // namespace juce } // namespace juce

View file

@ -62,7 +62,7 @@ static const uint8 invocationHandleByteCode[] =
CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \ CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \
CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V") CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
@ -116,7 +116,7 @@ struct SystemJavaClassComparator
}; };
//============================================================================== //==============================================================================
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n) JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const uint8* bc, size_t n)
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr) : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr)
{ {
SystemJavaClassComparator comparator; SystemJavaClassComparator comparator;
@ -552,12 +552,12 @@ static const uint8 javaFragmentOverlay[] =
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (construct, "<init>", "()V") \ METHOD (construct, "<init>", "()V") \
METHOD (close, "close", "()V") \ METHOD (close, "close", "()V") \
CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \ CALLBACK (generatedCallback<&FragmentOverlay::onActivityResultCallback>, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \
CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \ CALLBACK (generatedCallback<&FragmentOverlay::onCreatedCallback>, "onCreateNative", "(JLandroid/os/Bundle;)V") \
CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \ CALLBACK (generatedCallback<&FragmentOverlay::onStartCallback>, "onStartNative", "(J)V") \
CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V") CALLBACK (generatedCallback<&FragmentOverlay::onRequestPermissionsResultCallback>, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
@ -590,47 +590,39 @@ void FragmentOverlay::open()
env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get()); env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get());
} }
void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host, void FragmentOverlay::onCreatedCallback (JNIEnv* env, FragmentOverlay& t, jobject obj)
jint requestCode, jint resultCode, jobject data)
{ {
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) t.onCreated (LocalRef<jobject> { env->NewLocalRef (obj) });
myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
} }
void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle) void FragmentOverlay::onStartCallback (JNIEnv*, FragmentOverlay& t)
{ {
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host)) t.onStart();
myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle)));
} }
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)) Array<int> grantResults;
myself->onStart(); int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
}
void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode, if (n > 0)
jobjectArray jPermissions, jintArray jGrantResults)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
{ {
Array<int> grantResults; auto* data = env->GetIntArrayElements (jGrantResults, nullptr);
int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
if (n > 0) for (int i = 0; i < n; ++i)
{ grantResults.add (data[i]);
auto* data = env->GetIntArrayElements (jGrantResults, nullptr);
for (int i = 0; i < n; ++i) env->ReleaseIntArrayElements (jGrantResults, data, 0);
grantResults.add (data[i]);
env->ReleaseIntArrayElements (jGrantResults, data, 0);
}
myself->onRequestPermissionsResult (requestCode,
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
grantResults);
} }
t.onRequestPermissionsResult (requestCode,
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
grantResults);
}
void FragmentOverlay::onActivityResultCallback (JNIEnv* env, FragmentOverlay& t, jint requestCode, jint resultCode, jobject data)
{
t.onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
} }
jobject FragmentOverlay::getNativeHandle() jobject FragmentOverlay::getNativeHandle()

View file

@ -172,7 +172,7 @@ struct SystemJavaClassComparator;
class JNIClassBase class JNIClassBase
{ {
public: public:
JNIClassBase (const char* classPath, int minSDK, const void* byteCode, size_t byteCodeSize); JNIClassBase (const char* classPath, int minSDK, const uint8* byteCode, size_t byteCodeSize);
virtual ~JNIClassBase(); virtual ~JNIClassBase();
operator jclass() const noexcept { return classRef; } operator jclass() const noexcept { return classRef; }
@ -210,6 +210,9 @@ private:
}; };
//============================================================================== //==============================================================================
template <typename T, size_t N> constexpr auto numBytes (const T (&) [N]) { return N; }
constexpr auto numBytes (std::nullptr_t) { return static_cast<size_t> (0); }
#define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); #define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params);
#define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); #define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params);
#define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); #define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature);
@ -219,26 +222,26 @@ private:
#define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; #define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID;
#define DECLARE_JNI_CALLBACK(fieldID, stringName, signature) #define DECLARE_JNI_CALLBACK(fieldID, stringName, signature)
#define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData, byteCodeSize) \ #define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData) \
class CppClassName ## _Class : public JNIClassBase \ class CppClassName ## _Class : public JNIClassBase \
{ \ { \
public: \ public: \
CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, byteCodeSize) {} \ CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, numBytes (byteCodeData)) {} \
\ \
void initialiseFields (JNIEnv* env) \ void initialiseFields (JNIEnv* env) \
{ \ { \
Array<JNINativeMethod> callbacks; \ Array<JNINativeMethod> callbacks; \
JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \ JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \
resolveCallbacks (env, callbacks); \ resolveCallbacks (env, callbacks); \
} \ } \
\ \
JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \ JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \
}; \ }; \
static CppClassName ## _Class CppClassName; static inline const CppClassName ## _Class CppClassName;
//============================================================================== //==============================================================================
#define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \ #define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \
DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr, 0) DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr)
//============================================================================== //==============================================================================
#define DECLARE_JNI_CLASS(CppClassName, javaPath) \ #define DECLARE_JNI_CLASS(CppClassName, javaPath) \
@ -1005,20 +1008,20 @@ public:
const Array<int>& /*grantResults*/) {} const Array<int>& /*grantResults*/) {}
virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef<jobject> /*data*/) {} virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef<jobject> /*data*/) {}
/** @internal */
static void onCreatedCallback (JNIEnv*, FragmentOverlay&, jobject obj);
/** @internal */
static void onStartCallback (JNIEnv*, FragmentOverlay&);
/** @internal */
static void onRequestPermissionsResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jobjectArray jPermissions, jintArray jGrantResults);
/** @internal */
static void onActivityResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jint resultCode, jobject data);
protected: protected:
jobject getNativeHandle(); jobject getNativeHandle();
private: private:
GlobalRef native; GlobalRef native;
public:
/* internal: do not use */
static void onActivityResultNative (JNIEnv*, jobject, jlong, jint, jint, jobject);
static void onCreateNative (JNIEnv*, jobject, jlong, jobject);
static void onStartNative (JNIEnv*, jobject, jlong);
static void onRequestPermissionsResultNative (JNIEnv*, jobject, jlong, jint,
jobjectArray, jintArray);
}; };
//============================================================================== //==============================================================================
@ -1030,4 +1033,30 @@ void startAndroidActivityForResult (const LocalRef<jobject>& intent, int request
bool androidHasSystemFeature (const String& property); bool androidHasSystemFeature (const String& property);
String audioManagerGetProperty (const String& property); String audioManagerGetProperty (const String& property);
namespace detail
{
template <auto Fn, typename Result, typename Class, typename... Args>
inline constexpr auto generatedCallbackImpl =
juce::toFnPtr (JNICALL [] (JNIEnv* env, jobject, jlong host, Args... args) -> Result
{
if (auto* object = reinterpret_cast<Class*> (host))
return Fn (env, *object, args...);
return {};
});
template <auto Fn, typename Result, typename Class, typename... Args>
constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, Class&, Args...)) { return generatedCallbackImpl<Fn, Result, Class, Args...>; }
template <auto Fn, typename Result, typename Class, typename... Args>
constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, const Class&, Args...)) { return generatedCallbackImpl<Fn, Result, Class, Args...>; }
} // namespace detail
// Evaluates to a static function that forwards to the provided Fn, assuming that the
// 'host' argument points to an object on which it is valid to call Fn
template <auto Fn>
inline constexpr auto generatedCallback = detail::generateCallbackImpl<Fn> (Fn);
} // namespace juce } // namespace juce

View file

@ -195,7 +195,7 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
METHOD (isExhausted, "isExhausted", "()Z") \ METHOD (isExhausted, "isExhausted", "()Z") \
METHOD (setPosition, "setPosition", "(J)Z") \ METHOD (setPosition, "setPosition", "(J)Z") \
DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream)) DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
//============================================================================== //==============================================================================

View file

@ -342,15 +342,6 @@ namespace detail
{ {
return joinCompileTimeStr (v, makeCompileTimeStr (others...)); return joinCompileTimeStr (v, makeCompileTimeStr (others...));
} }
template <typename Functor, typename Return, typename... Args>
static constexpr auto toFnPtr (Functor functor, Return (Functor::*) (Args...) const)
{
return static_cast<Return (*) (Args...)> (functor);
}
template <typename Functor>
static constexpr auto toFnPtr (Functor functor) { return toFnPtr (functor, &Functor::operator()); }
} // namespace detail } // namespace detail
//============================================================================== //==============================================================================
@ -396,7 +387,7 @@ struct ObjCClass
} }
template <typename Fn> template <typename Fn>
void addMethod (SEL selector, Fn callbackFn) { addMethod (selector, detail::toFnPtr (callbackFn)); } void addMethod (SEL selector, Fn callbackFn) { addMethod (selector, toFnPtr (callbackFn)); }
template <typename Result, typename... Args> template <typename Result, typename... Args>
void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...))

View file

@ -336,6 +336,28 @@ namespace juce
#include "native/juce_linux_FileChooser.cpp" #include "native/juce_linux_FileChooser.cpp"
#elif JUCE_ANDROID #elif JUCE_ANDROID
namespace juce
{
static jobject makeAndroidRect (Rectangle<int> r)
{
return getEnv()->NewObject (AndroidRect,
AndroidRect.constructor,
r.getX(),
r.getY(),
r.getRight(),
r.getBottom());
}
static jobject makeAndroidPoint (Point<int> p)
{
return getEnv()->NewObject (AndroidPoint,
AndroidPoint.create,
p.getX(),
p.getY());
}
} // namespace juce
#include "juce_core/files/juce_common_MimeTypes.h" #include "juce_core/files/juce_common_MimeTypes.h"
#include "native/accessibility/juce_android_Accessibility.cpp" #include "native/accessibility/juce_android_Accessibility.cpp"
#include "native/juce_android_Windowing.cpp" #include "native/juce_android_Windowing.cpp"

View file

@ -26,7 +26,6 @@
namespace juce namespace juce
{ {
//==============================================================================
/** /**
An abstract base class which can be implemented by components that function as An abstract base class which can be implemented by components that function as
text editors. text editors.

View file

@ -297,21 +297,13 @@ public:
{ {
const auto scale = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale; const auto scale = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
const auto screenBounds = accessibilityHandler.getComponent().getScreenBounds() * scale; LocalRef<jobject> screenBounds (makeAndroidRect (accessibilityHandler.getComponent().getScreenBounds() * scale));
LocalRef<jobject> rect (env->NewObject (AndroidRect, AndroidRect.constructor, env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInScreen, screenBounds.get());
screenBounds.getX(), screenBounds.getY(),
screenBounds.getRight(), screenBounds.getBottom()));
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInScreen, rect.get()); LocalRef<jobject> boundsInParent (makeAndroidRect (accessibilityHandler.getComponent().getBoundsInParent() * scale));
const auto boundsInParent = accessibilityHandler.getComponent().getBoundsInParent() * scale; env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInParent, boundsInParent.get());
rect = LocalRef<jobject> (env->NewObject (AndroidRect, AndroidRect.constructor,
boundsInParent.getX(), boundsInParent.getY(),
boundsInParent.getRight(), boundsInParent.getBottom()));
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInParent, rect.get());
} }
const auto state = accessibilityHandler.getCurrentState(); const auto state = accessibilityHandler.getCurrentState();

View file

@ -32,9 +32,19 @@ import android.graphics.Canvas;
import android.graphics.ColorMatrix; import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter; import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.Pair;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -51,6 +61,7 @@ import android.view.inputmethod.InputMethodManager;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public final class ComponentPeerView extends ViewGroup public final class ComponentPeerView extends ViewGroup
@ -63,9 +74,10 @@ public final class ComponentPeerView extends ViewGroup
if (Application.class.isInstance (context)) if (Application.class.isInstance (context))
{ {
((Application) context).registerActivityLifecycleCallbacks (this); ((Application) context).registerActivityLifecycleCallbacks (this);
} else }
else
{ {
((Application) context.getApplicationContext ()).registerActivityLifecycleCallbacks (this); ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks (this);
} }
this.host = host; this.host = host;
@ -77,7 +89,7 @@ public final class ComponentPeerView extends ViewGroup
setOnFocusChangeListener (this); setOnFocusChangeListener (this);
// swap red and blue colours to match internal opengl texture format // swap red and blue colours to match internal opengl texture format
ColorMatrix colorMatrix = new ColorMatrix (); ColorMatrix colorMatrix = new ColorMatrix();
float[] colorTransform = {0, 0, 1.0f, 0, 0, float[] colorTransform = {0, 0, 1.0f, 0, 0,
0, 1.0f, 0, 0, 0, 0, 1.0f, 0, 0, 0,
@ -91,10 +103,12 @@ public final class ComponentPeerView extends ViewGroup
try try
{ {
method = getClass ().getMethod ("setLayerType", int.class, Paint.class); method = getClass().getMethod ("setLayerType", int.class, Paint.class);
} catch (SecurityException e) }
catch (SecurityException e)
{ {
} catch (NoSuchMethodException e) }
catch (NoSuchMethodException e)
{ {
} }
@ -104,11 +118,14 @@ public final class ComponentPeerView extends ViewGroup
{ {
int layerTypeNone = 0; int layerTypeNone = 0;
method.invoke (this, layerTypeNone, null); method.invoke (this, layerTypeNone, null);
} catch (java.lang.IllegalArgumentException e) }
catch (java.lang.IllegalArgumentException e)
{ {
} catch (java.lang.IllegalAccessException e) }
catch (java.lang.IllegalAccessException e)
{ {
} catch (java.lang.reflect.InvocationTargetException e) }
catch (java.lang.reflect.InvocationTargetException e)
{ {
} }
} }
@ -116,7 +133,7 @@ public final class ComponentPeerView extends ViewGroup
Choreographer.getInstance().postFrameCallback (this); Choreographer.getInstance().postFrameCallback (this);
} }
public void clear () public void clear()
{ {
host = 0; host = 0;
} }
@ -147,14 +164,14 @@ public final class ComponentPeerView extends ViewGroup
} }
@Override @Override
public boolean isOpaque () public boolean isOpaque()
{ {
return opaque; return opaque;
} }
private final boolean opaque; private final boolean opaque;
private long host; private long host;
private final Paint paint = new Paint (); private final Paint paint = new Paint();
//============================================================================== //==============================================================================
private native void handleMouseDown (long host, int index, float x, float y, long time); private native void handleMouseDown (long host, int index, float x, float y, long time);
@ -168,25 +185,25 @@ public final class ComponentPeerView extends ViewGroup
if (host == 0) if (host == 0)
return false; return false;
int action = event.getAction (); int action = event.getAction();
long time = event.getEventTime (); long time = event.getEventTime();
switch (action & MotionEvent.ACTION_MASK) switch (action & MotionEvent.ACTION_MASK)
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); handleMouseDown (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time);
return true; return true;
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); handleMouseUp (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time);
return true; return true;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
{ {
handleMouseDrag (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); handleMouseDrag (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time);
int n = event.getPointerCount (); int n = event.getPointerCount();
if (n > 1) if (n > 1)
{ {
@ -206,8 +223,9 @@ public final class ComponentPeerView extends ViewGroup
if (i == 0) if (i == 0)
{ {
handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); handleMouseUp (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time);
} else }
else
{ {
int point[] = new int[2]; int point[] = new int[2];
getLocationOnScreen (point); getLocationOnScreen (point);
@ -223,8 +241,9 @@ public final class ComponentPeerView extends ViewGroup
if (i == 0) if (i == 0)
{ {
handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time); handleMouseDown (host, event.getPointerId (0), event.getRawX(), event.getRawY(), time);
} else }
else
{ {
int point[] = new int[2]; int point[] = new int[2];
getLocationOnScreen (point); getLocationOnScreen (point);
@ -253,32 +272,132 @@ public final class ComponentPeerView extends ViewGroup
return false; return false;
} }
//==============================================================================
public static class TextInputTarget
{
public TextInputTarget (long owner) { host = owner; }
public boolean isTextInputActive() { return ComponentPeerView.textInputTargetIsTextInputActive (host); }
public int getHighlightedRegionBegin() { return ComponentPeerView.textInputTargetGetHighlightedRegionBegin (host); }
public int getHighlightedRegionEnd() { return ComponentPeerView.textInputTargetGetHighlightedRegionEnd (host); }
public void setHighlightedRegion (int b, int e) { ComponentPeerView.textInputTargetSetHighlightedRegion (host, b, e); }
public String getTextInRange (int b, int e) { return ComponentPeerView.textInputTargetGetTextInRange (host, b, e); }
public void insertTextAtCaret (String text) { ComponentPeerView.textInputTargetInsertTextAtCaret (host, text); }
public int getCaretPosition() { return ComponentPeerView.textInputTargetGetCaretPosition (host); }
public int getTotalNumChars() { return ComponentPeerView.textInputTargetGetTotalNumChars (host); }
public int getCharIndexForPoint (Point point) { return ComponentPeerView.textInputTargetGetCharIndexForPoint (host, point); }
public int getKeyboardType() { return ComponentPeerView.textInputTargetGetKeyboardType (host); }
public void setTemporaryUnderlining (List<Pair<Integer, Integer>> list) { ComponentPeerView.textInputTargetSetTemporaryUnderlining (host, list); }
//==============================================================================
private final long host;
}
private native static boolean textInputTargetIsTextInputActive (long host);
private native static int textInputTargetGetHighlightedRegionBegin (long host);
private native static int textInputTargetGetHighlightedRegionEnd (long host);
private native static void textInputTargetSetHighlightedRegion (long host, int begin, int end);
private native static String textInputTargetGetTextInRange (long host, int begin, int end);
private native static void textInputTargetInsertTextAtCaret (long host, String text);
private native static int textInputTargetGetCaretPosition (long host);
private native static int textInputTargetGetTotalNumChars (long host);
private native static int textInputTargetGetCharIndexForPoint (long host, Point point);
private native static int textInputTargetGetKeyboardType (long host);
private native static void textInputTargetSetTemporaryUnderlining (long host, List<Pair<Integer, Integer>> list);
private native long getFocusedTextInputTargetPointer (long host);
private TextInputTarget getFocusedTextInputTarget (long host)
{
final long ptr = getFocusedTextInputTargetPointer (host);
return ptr != 0 ? new TextInputTarget (ptr) : null;
}
//============================================================================== //==============================================================================
private native void handleKeyDown (long host, int keycode, int textchar, int kbFlags); private native void handleKeyDown (long host, int keycode, int textchar, int kbFlags);
private native void handleKeyUp (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar);
private native void handleBackButton (long host); private native void handleBackButton (long host);
private native void handleKeyboardHidden (long host); private native void handleKeyboardHidden (long host);
public void showKeyboard (String type) private static int getInputTypeForJuceVirtualKeyboardType (int type)
{ {
InputMethodManager imm = (InputMethodManager) getContext ().getSystemService (Context.INPUT_METHOD_SERVICE); switch (type)
if (imm != null)
{ {
if (type.length () > 0) case 0: // textKeyboard
{ return InputType.TYPE_CLASS_TEXT
imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); | InputType.TYPE_TEXT_VARIATION_NORMAL
imm.setInputMethod (getWindowToken (), type); | InputType.TYPE_TEXT_FLAG_MULTI_LINE
keyboardDismissListener.startListening (); | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
} else case 1: // numericKeyboard
{ return InputType.TYPE_CLASS_NUMBER
imm.hideSoftInputFromWindow (getWindowToken (), 0); | InputType.TYPE_NUMBER_VARIATION_NORMAL;
keyboardDismissListener.stopListening (); 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;
} }
public void backButtonPressed () 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 hideKeyboard()
{
InputMethodManager imm = getInputMethodManager();
if (imm == null)
return;
imm.hideSoftInputFromWindow (getWindowToken(), 0);
keyboardDismissListener.stopListening();
}
public void backButtonPressed()
{ {
if (host == 0) if (host == 0)
return; return;
@ -292,6 +411,11 @@ public final class ComponentPeerView extends ViewGroup
if (host == 0) if (host == 0)
return false; return false;
// The key event may move the cursor, or in some cases it might enter characters (e.g.
// digits). In this case, we need to reset the IME so that it's aware of the new contents
// of the TextInputTarget.
closeInputMethodContext();
switch (keyCode) switch (keyCode)
{ {
case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_UP:
@ -299,7 +423,7 @@ public final class ComponentPeerView extends ViewGroup
return super.onKeyDown (keyCode, event); return super.onKeyDown (keyCode, event);
case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BACK:
{ {
backButtonPressed (); backButtonPressed();
return true; return true;
} }
@ -309,8 +433,9 @@ public final class ComponentPeerView extends ViewGroup
handleKeyDown (host, handleKeyDown (host,
keyCode, keyCode,
event.getUnicodeChar (), event.getUnicodeChar(),
event.getMetaState ()); event.getMetaState());
return true; return true;
} }
@ -320,7 +445,7 @@ public final class ComponentPeerView extends ViewGroup
if (host == 0) if (host == 0)
return false; return false;
handleKeyUp (host, keyCode, event.getUnicodeChar ()); handleKeyUp (host, keyCode, event.getUnicodeChar());
return true; return true;
} }
@ -330,17 +455,17 @@ public final class ComponentPeerView extends ViewGroup
if (host == 0) if (host == 0)
return false; return false;
if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction () != KeyEvent.ACTION_MULTIPLE) if (keyCode != KeyEvent.KEYCODE_UNKNOWN || (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && event.getAction() != KeyEvent.ACTION_MULTIPLE))
return super.onKeyMultiple (keyCode, count, event); return super.onKeyMultiple (keyCode, count, event);
if (event.getCharacters () != null) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && event.getCharacters() != null)
{ {
int utf8Char = event.getCharacters ().codePointAt (0); int utf8Char = event.getCharacters().codePointAt (0);
handleKeyDown (host, handleKeyDown (host,
keyCode, keyCode,
utf8Char, utf8Char,
event.getMetaState ()); event.getMetaState());
return true; return true;
} }
@ -355,39 +480,40 @@ public final class ComponentPeerView extends ViewGroup
view = viewToUse; view = viewToUse;
} }
private void startListening () private void startListening()
{ {
view.getViewTreeObserver ().addOnGlobalLayoutListener (viewTreeObserver); view.getViewTreeObserver().addOnGlobalLayoutListener (viewTreeObserver);
} }
private void stopListening () private void stopListening()
{ {
view.getViewTreeObserver ().removeGlobalOnLayoutListener (viewTreeObserver); view.getViewTreeObserver().removeOnGlobalLayoutListener (viewTreeObserver);
} }
private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
{ {
TreeObserver () TreeObserver()
{ {
keyboardShown = false; keyboardShown = false;
} }
@Override @Override
public void onGlobalLayout () public void onGlobalLayout()
{ {
Rect r = new Rect (); Rect r = new Rect();
View parentView = getRootView (); View parentView = getRootView();
int diff; int diff;
if (parentView == null) if (parentView == null)
{ {
getWindowVisibleDisplayFrame (r); getWindowVisibleDisplayFrame (r);
diff = getHeight () - (r.bottom - r.top); diff = getHeight() - (r.bottom - r.top);
} else }
else
{ {
parentView.getWindowVisibleDisplayFrame (r); parentView.getWindowVisibleDisplayFrame (r);
diff = parentView.getHeight () - (r.bottom - r.top); diff = parentView.getHeight() - (r.bottom - r.top);
} }
// Arbitrary threshold, surely keyboard would take more than 20 pix. // Arbitrary threshold, surely keyboard would take more than 20 pix.
@ -397,7 +523,7 @@ public final class ComponentPeerView extends ViewGroup
handleKeyboardHidden (view.host); handleKeyboardHidden (view.host);
} }
if (!keyboardShown && diff > 20) if (! keyboardShown && diff > 20)
keyboardShown = true; keyboardShown = true;
} }
@ -405,26 +531,219 @@ public final class ComponentPeerView extends ViewGroup
} }
private final ComponentPeerView view; private final ComponentPeerView view;
private final TreeObserver viewTreeObserver = new TreeObserver (); private final TreeObserver viewTreeObserver = new TreeObserver();
} }
private final KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this); private final KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this);
// this is here to make keyboard entry work on a Galaxy Tab2 10.1 //==============================================================================
// This implementation is quite similar to the ChangeListener in Android's built-in TextView.
private static final class ChangeWatcher implements SpanWatcher, TextWatcher
{
public ChangeWatcher (ComponentPeerView viewIn, Editable editableIn, TextInputTarget targetIn)
{
view = viewIn;
editable = editableIn;
target = targetIn;
updateEditableSelectionFromTarget (editable, target);
}
@Override
public void onSpanAdded (Spannable text, Object what, int start, int end)
{
updateTargetRangesFromEditable (editable, target);
}
@Override
public void onSpanRemoved (Spannable text, Object what, int start, int end)
{
updateTargetRangesFromEditable (editable, target);
}
@Override
public void onSpanChanged (Spannable text, Object what, int ostart, int oend, int nstart, int nend)
{
updateTargetRangesFromEditable (editable, target);
}
@Override
public void afterTextChanged (Editable s)
{
}
@Override
public void beforeTextChanged (CharSequence s, int start, int count, int after)
{
contentsBeforeChange = s.toString();
}
@Override
public void onTextChanged (CharSequence s, int start, int before, int count)
{
if (editable != s || contentsBeforeChange == null)
return;
final String newText = s.subSequence (start, start + count).toString();
int code = 0;
if (newText.endsWith ("\n") || newText.endsWith ("\r"))
code = KeyEvent.KEYCODE_ENTER;
if (newText.endsWith ("\t"))
code = KeyEvent.KEYCODE_TAB;
target.setHighlightedRegion (contentsBeforeChange.codePointCount (0, start),
contentsBeforeChange.codePointCount (0, start + before));
target.insertTextAtCaret (code != 0 ? newText.substring (0, newText.length() - 1)
: newText);
// Treating return/tab as individual keypresses rather than part of the composition
// sequence allows TextEditor onReturn and onTab to work as expected.
if (code != 0)
view.onKeyDown (code, new KeyEvent (KeyEvent.ACTION_DOWN, code));
updateTargetRangesFromEditable (editable, target);
contentsBeforeChange = null;
}
private static void updateEditableSelectionFromTarget (Editable editable, TextInputTarget text)
{
final int start = text.getHighlightedRegionBegin();
final int end = text.getHighlightedRegionEnd();
if (start < 0 || end < 0)
return;
final String string = editable.toString();
Selection.setSelection (editable,
string.offsetByCodePoints (0, start),
string.offsetByCodePoints (0, end));
}
private static void updateTargetSelectionFromEditable (Editable editable, TextInputTarget target)
{
final int start = Selection.getSelectionStart (editable);
final int end = Selection.getSelectionEnd (editable);
if (start < 0 || end < 0)
return;
final String string = editable.toString();
target.setHighlightedRegion (string.codePointCount (0, start),
string.codePointCount (0, end));
}
private static List<Pair<Integer, Integer>> getUnderlinedRanges (Editable editable)
{
final int start = BaseInputConnection.getComposingSpanStart (editable);
final int end = BaseInputConnection.getComposingSpanEnd (editable);
if (start < 0 || end < 0)
return null;
final String string = editable.toString();
final ArrayList<Pair<Integer, Integer>> pairs = new ArrayList<>();
pairs.add (new Pair<> (string.codePointCount (0, start), string.codePointCount (0, end)));
return pairs;
}
private static void updateTargetCompositionRangesFromEditable (Editable editable, TextInputTarget target)
{
target.setTemporaryUnderlining (getUnderlinedRanges (editable));
}
private static void updateTargetRangesFromEditable (Editable editable, TextInputTarget target)
{
updateTargetSelectionFromEditable (editable, target);
updateTargetCompositionRangesFromEditable (editable, target);
}
private final ComponentPeerView view;
private final TextInputTarget target;
private final Editable editable;
private String contentsBeforeChange;
}
private static final class Connection extends BaseInputConnection
{
Connection (ComponentPeerView viewIn, boolean fullEditor, TextInputTarget targetIn)
{
super (viewIn, fullEditor);
view = viewIn;
target = targetIn;
}
@Override
public Editable getEditable()
{
if (cached != null)
return cached;
if (target == null)
return cached = super.getEditable();
int length = target.getTotalNumChars();
String initialText = target.getTextInRange (0, length);
cached = new SpannableStringBuilder (initialText);
// Span the entire range of text, so that we pick up changes at any location.
// Use cached.length rather than target.getTotalNumChars here, because this
// range is in UTF-16 code units, rather than code points.
changeWatcher = new ChangeWatcher (view, cached, target);
cached.setSpan (changeWatcher, 0, cached.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return cached;
}
/** Call this to stop listening for selection/composition updates.
We do this before closing the current input method context (e.g. when the user
taps on a text view to move the cursor), because otherwise the input system
might send another round of notifications *during* the restartInput call, after we've
requested that the input session should end.
*/
@Override
public void closeConnection()
{
if (cached != null && changeWatcher != null)
cached.removeSpan (changeWatcher);
cached = null;
target = null;
super.closeConnection();
}
private ComponentPeerView view;
private TextInputTarget target;
private Editable cached;
private ChangeWatcher changeWatcher;
}
@Override @Override
public InputConnection onCreateInputConnection (EditorInfo outAttrs) public InputConnection onCreateInputConnection (EditorInfo outAttrs)
{ {
TextInputTarget focused = getFocusedTextInputTarget (host);
outAttrs.actionLabel = ""; outAttrs.actionLabel = "";
outAttrs.hintText = ""; outAttrs.hintText = "";
outAttrs.initialCapsMode = 0; outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; outAttrs.initialSelStart = focused != null ? focused.getHighlightedRegionBegin() : -1;
outAttrs.initialSelEnd = focused != null ? focused.getHighlightedRegionEnd() : -1;
outAttrs.label = ""; outAttrs.label = "";
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; outAttrs.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED
outAttrs.inputType = InputType.TYPE_NULL; | EditorInfo.IME_FLAG_NO_EXTRACT_UI
| EditorInfo.IME_FLAG_NO_ENTER_ACTION;
outAttrs.inputType = focused != null ? getInputTypeForJuceVirtualKeyboardType (focused.getKeyboardType())
: 0;
return new BaseInputConnection (this, false); cachedConnection = new Connection (this, true, focused);
return cachedConnection;
} }
private Connection cachedConnection;
//============================================================================== //==============================================================================
@Override @Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) protected void onSizeChanged (int w, int h, int oldw, int oldh)
@ -463,11 +782,13 @@ public final class ComponentPeerView extends ViewGroup
Method systemUIVisibilityMethod = null; Method systemUIVisibilityMethod = null;
try try
{ {
systemUIVisibilityMethod = this.getClass ().getMethod ("setSystemUiVisibility", int.class); systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class);
} catch (SecurityException e) }
catch (SecurityException e)
{ {
return; return;
} catch (NoSuchMethodException e) }
catch (NoSuchMethodException e)
{ {
return; return;
} }
@ -476,18 +797,21 @@ public final class ComponentPeerView extends ViewGroup
try try
{ {
systemUIVisibilityMethod.invoke (this, visibility); systemUIVisibilityMethod.invoke (this, visibility);
} catch (java.lang.IllegalArgumentException e) }
catch (java.lang.IllegalArgumentException e)
{ {
} catch (java.lang.IllegalAccessException e) }
catch (java.lang.IllegalAccessException e)
{ {
} catch (java.lang.reflect.InvocationTargetException e) }
catch (java.lang.reflect.InvocationTargetException e)
{ {
} }
} }
public boolean isVisible () public boolean isVisible()
{ {
return getVisibility () == VISIBLE; return getVisibility() == VISIBLE;
} }
public void setVisible (boolean b) public void setVisible (boolean b)
@ -572,11 +896,22 @@ public final class ComponentPeerView extends ViewGroup
if (host == 0) if (host == 0)
return null; return null;
final AccessibilityNodeInfo nodeInfo = AccessibilityNodeInfo.obtain (view, virtualViewId); final AccessibilityNodeInfo nodeInfo;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
{
nodeInfo = new AccessibilityNodeInfo (view, virtualViewId);
}
else
{
nodeInfo = AccessibilityNodeInfo.obtain (view, virtualViewId);
}
if (! populateAccessibilityNodeInfo (host, virtualViewId, nodeInfo)) if (! populateAccessibilityNodeInfo (host, virtualViewId, nodeInfo))
{ {
nodeInfo.recycle(); if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R)
nodeInfo.recycle();
return null; return null;
} }
@ -617,10 +952,10 @@ public final class ComponentPeerView extends ViewGroup
} }
private final JuceAccessibilityNodeProvider nodeProvider = new JuceAccessibilityNodeProvider (this); private final JuceAccessibilityNodeProvider nodeProvider = new JuceAccessibilityNodeProvider (this);
private final AccessibilityManager accessibilityManager = (AccessibilityManager) getContext ().getSystemService (Context.ACCESSIBILITY_SERVICE); private final AccessibilityManager accessibilityManager = (AccessibilityManager) getContext().getSystemService (Context.ACCESSIBILITY_SERVICE);
@Override @Override
public AccessibilityNodeProvider getAccessibilityNodeProvider () public AccessibilityNodeProvider getAccessibilityNodeProvider()
{ {
return nodeProvider; return nodeProvider;
} }

View file

@ -34,7 +34,6 @@ public final class JuceContentProviderFileObserver extends FileObserver
public JuceContentProviderFileObserver (long hostToUse, String path, int mask) public JuceContentProviderFileObserver (long hostToUse, String path, int mask)
{ {
super (path, mask); super (path, mask);
host = hostToUse; host = hostToUse;
} }

View file

@ -101,7 +101,7 @@ public:
class Owner class Owner
{ {
public: public:
virtual ~Owner() {} virtual ~Owner() = default;
virtual void cursorClosed (const AndroidContentSharerCursor&) = 0; virtual void cursorClosed (const AndroidContentSharerCursor&) = 0;
}; };
@ -121,9 +121,9 @@ public:
jobject getNativeCursor() { return cursor.get(); } jobject getNativeCursor() { return cursor.get(); }
void cursorClosed() static void cursorClosed (JNIEnv*, AndroidContentSharerCursor& t)
{ {
MessageManager::callAsync ([this] { owner.cursorClosed (*this); }); MessageManager::callAsync ([&t] { t.owner.cursorClosed (t); });
} }
void addRow (LocalRef<jobjectArray>& values) void addRow (LocalRef<jobjectArray>& values)
@ -141,20 +141,12 @@ private:
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \ METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
METHOD (constructor, "<init>", "(J[Ljava/lang/String;)V") \ METHOD (constructor, "<init>", "(J[Ljava/lang/String;)V") \
CALLBACK (contentSharerCursorClosed, "contentSharerCursorClosed", "(J)V") \ CALLBACK (generatedCallback<&AndroidContentSharerCursor::cursorClosed>, "contentSharerCursorClosed", "(J)V") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderCursor, "com/rmsl/juce/JuceContentProviderCursor", 16, javaJuceContentProviderCursor, sizeof (javaJuceContentProviderCursor)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderCursor, "com/rmsl/juce/JuceContentProviderCursor", 16, javaJuceContentProviderCursor)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
static void JNICALL contentSharerCursorClosed (JNIEnv*, jobject, jlong host)
{
if (auto* myself = reinterpret_cast<AndroidContentSharerCursor*> (host))
myself->cursorClosed();
}
}; };
AndroidContentSharerCursor::JuceContentProviderCursor_Class AndroidContentSharerCursor::JuceContentProviderCursor;
//============================================================================== //==============================================================================
class AndroidContentSharerFileObserver class AndroidContentSharerFileObserver
{ {
@ -228,20 +220,17 @@ private:
METHOD (constructor, "<init>", "(JLjava/lang/String;I)V") \ METHOD (constructor, "<init>", "(JLjava/lang/String;I)V") \
METHOD (startWatching, "startWatching", "()V") \ METHOD (startWatching, "startWatching", "()V") \
METHOD (stopWatching, "stopWatching", "()V") \ METHOD (stopWatching, "stopWatching", "()V") \
CALLBACK (contentSharerFileObserverEvent, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \ CALLBACK (generatedCallback<&AndroidContentSharerFileObserver::onFileEventCallback>, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderFileObserver, "com/rmsl/juce/JuceContentProviderFileObserver", 16, javaJuceContentProviderFileObserver, sizeof (javaJuceContentProviderFileObserver)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderFileObserver, "com/rmsl/juce/JuceContentProviderFileObserver", 16, javaJuceContentProviderFileObserver)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
static void JNICALL contentSharerFileObserverEvent (JNIEnv*, jobject /*fileObserver*/, jlong host, int event, jstring path) static void onFileEventCallback (JNIEnv*, AndroidContentSharerFileObserver& t, jint event, jstring path)
{ {
if (auto* myself = reinterpret_cast<AndroidContentSharerFileObserver*> (host)) t.onFileEvent (event, LocalRef<jstring> (path));
myself->onFileEvent (event, LocalRef<jstring> (path));
} }
}; };
AndroidContentSharerFileObserver::JuceContentProviderFileObserver_Class AndroidContentSharerFileObserver::JuceContentProviderFileObserver;
//============================================================================== //==============================================================================
class AndroidContentSharerPrepareFilesThread : private Thread class AndroidContentSharerPrepareFilesThread : private Thread
{ {
@ -891,6 +880,4 @@ ContentSharer::Pimpl* ContentSharer::createPimpl()
return new ContentSharerNativeImpl (*this); return new ContentSharerNativeImpl (*this);
} }
ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider;
} // namespace juce } // namespace juce

File diff suppressed because it is too large Load diff

View file

@ -948,6 +948,11 @@ TextEditor::TextEditor (const String& name, juce_wchar passwordChar)
TextEditor::~TextEditor() TextEditor::~TextEditor()
{ {
giveAwayKeyboardFocus();
if (auto* peer = getPeer())
peer->refreshTextInputTarget();
textValue.removeListener (textHolder); textValue.removeListener (textHolder);
textValue.referTo (Value()); textValue.referTo (Value());
@ -1240,7 +1245,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage)
textValue = newText; textValue = newText;
auto oldCursorPos = caretPosition; auto oldCursorPos = caretPosition;
bool cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); auto cursorWasAtEnd = oldCursorPos >= getTotalNumChars();
clearInternal (nullptr); clearInternal (nullptr);
insert (newText, 0, currentFont, findColour (textColourId), nullptr, caretPosition); insert (newText, 0, currentFont, findColour (textColourId), nullptr, caretPosition);
@ -1376,26 +1381,23 @@ void TextEditor::repaintText (Range<int> range)
} }
//============================================================================== //==============================================================================
void TextEditor::moveCaret (int newCaretPos) void TextEditor::moveCaret (const int newCaretPos)
{ {
if (newCaretPos < 0) const auto clamped = std::clamp (newCaretPos, 0, getTotalNumChars());
newCaretPos = 0;
else
newCaretPos = jmin (newCaretPos, getTotalNumChars());
if (newCaretPos != getCaretPosition()) if (clamped == getCaretPosition())
{ return;
caretPosition = newCaretPos;
if (hasKeyboardFocus (false)) caretPosition = clamped;
textHolder->restartTimer();
scrollToMakeSureCursorIsVisible(); if (hasKeyboardFocus (false))
updateCaretPosition(); textHolder->restartTimer();
if (auto* handler = getAccessibilityHandler()) scrollToMakeSureCursorIsVisible();
handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged); updateCaretPosition();
}
if (auto* handler = getAccessibilityHandler())
handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged);
} }
int TextEditor::getCaretPosition() const int TextEditor::getCaretPosition() const

View file

@ -182,7 +182,6 @@ bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textChar
textCharacter)); textCharacter));
} }
bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo) bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
{ {
bool keyWasUsed = false; bool keyWasUsed = false;
@ -607,7 +606,7 @@ void ComponentPeer::forceDisplayUpdate()
Desktop::getInstance().displays->refresh(); Desktop::getInstance().displays->refresh();
} }
void ComponentPeer::globalFocusChanged (Component*) void ComponentPeer::globalFocusChanged ([[maybe_unused]] Component* comp)
{ {
refreshTextInputTarget(); refreshTextInputTarget();
} }

View file

@ -476,6 +476,11 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons
CodeEditorComponent::~CodeEditorComponent() CodeEditorComponent::~CodeEditorComponent()
{ {
giveAwayKeyboardFocus();
if (auto* peer = getPeer())
peer->refreshTextInputTarget();
document.removeListener (pimpl.get()); document.removeListener (pimpl.get());
} }

View file

@ -1549,8 +1549,6 @@ struct JuceFirebaseInstanceIdService
instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token))); instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token)));
} }
}; };
JuceFirebaseInstanceIdService::InstanceIdService_Class JuceFirebaseInstanceIdService::InstanceIdService;
#endif #endif
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
@ -1592,8 +1590,6 @@ struct JuceFirebaseMessagingService
LocalRef<jstring> (static_cast<jstring> (error))); LocalRef<jstring> (static_cast<jstring> (error)));
} }
}; };
JuceFirebaseMessagingService::MessagingService_Class JuceFirebaseMessagingService::MessagingService;
#endif #endif
//============================================================================== //==============================================================================

View file

@ -29,7 +29,7 @@ namespace juce
//============================================================================== //==============================================================================
// This byte-code is generated from native/java/com/rmsl/juce/JuceWebView.java with min sdk version 16 // This byte-code is generated from native/java/com/rmsl/juce/JuceWebView.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code. // See juce_core/native/java/README.txt on how to generate this byte-code.
static const unsigned char JuceWebView16ByteCode[] = static const uint8 JuceWebView16ByteCode[] =
{31,139,8,8,150,114,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,49,54,66,121,116,101,67,111,100,101,46,100,101,120,0,125, {31,139,8,8,150,114,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,49,54,66,121,116,101,67,111,100,101,46,100,101,120,0,125,
150,93,108,20,85,20,199,207,124,236,78,119,218,110,183,5,74,191,40,109,69,168,72,89,176,162,165,11,88,40,159,101,81,161,88,226, 150,93,108,20,85,20,199,207,124,236,78,119,218,110,183,5,74,191,40,109,69,168,72,89,176,162,165,11,88,40,159,101,81,161,88,226,
106,34,211,221,107,59,101,118,102,153,153,109,27,67,16,161,137,134,240,96,4,222,72,140,9,18,35,62,18,195,131,15,4,53,250,226,155, 106,34,211,221,107,59,101,118,102,153,153,109,27,67,16,161,137,134,240,96,4,222,72,140,9,18,35,62,18,195,131,15,4,53,250,226,155,
@ -81,7 +81,7 @@ static const unsigned char JuceWebView16ByteCode[] =
//============================================================================== //==============================================================================
// This byte-code is generated from native/javacore/app/com/rmsl/juce/JuceWebView21.java with min sdk version 21 // This byte-code is generated from native/javacore/app/com/rmsl/juce/JuceWebView21.java with min sdk version 21
// See juce_core/native/java/README.txt on how to generate this byte-code. // See juce_core/native/java/README.txt on how to generate this byte-code.
static const unsigned char JuceWebView21ByteCode[] = static const uint8 JuceWebView21ByteCode[] =
{31,139,8,8,45,103,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,50,49,46,100,101,120,0,141,151,93,140,27,87,21,199,207, {31,139,8,8,45,103,161,94,0,3,74,117,99,101,87,101,98,86,105,101,119,50,49,46,100,101,120,0,141,151,93,140,27,87,21,199,207,
204,216,30,219,99,59,182,55,251,145,143,221,110,210,173,178,105,154,186,155,164,52,169,211,106,241,38,219,221,48,41,52,155,108, 204,216,30,219,99,59,182,55,251,145,143,221,110,210,173,178,105,154,186,155,164,52,169,211,106,241,38,219,221,48,41,52,155,108,
138,43,85,154,181,47,235,73,188,51,206,204,120,119,65,162,132,80,148,138,34,148,168,20,181,125,129,135,16,129,4,18,168,125,136, 138,43,85,154,181,47,235,73,188,51,206,204,120,119,65,162,132,80,148,138,34,148,168,20,181,125,129,135,16,129,4,18,168,125,136,
@ -479,82 +479,62 @@ private:
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
METHOD (hostDeleted, "hostDeleted", "()V") \ METHOD (hostDeleted, "hostDeleted", "()V") \
CALLBACK (webViewReceivedHttpError, "webViewReceivedHttpError", "(JLandroid/webkit/WebView;Landroid/webkit/WebResourceRequest;Landroid/webkit/WebResourceResponse;)V") \ CALLBACK (generatedCallback<&Pimpl::webViewReceivedHttpError>, "webViewReceivedHttpError", "(JLandroid/webkit/WebView;Landroid/webkit/WebResourceRequest;Landroid/webkit/WebResourceResponse;)V") \
CALLBACK (webViewPageLoadStarted, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ CALLBACK (generatedCallback<&Pimpl::webViewPageLoadStarted>, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \
CALLBACK (webViewPageLoadFinished, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ CALLBACK (generatedCallback<&Pimpl::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::webViewReceivedSslError>, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient21, "com/rmsl/juce/JuceWebView21$Client", 21, JuceWebView21ByteCode, sizeof (JuceWebView21ByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient21, "com/rmsl/juce/JuceWebView21$Client", 21, JuceWebView21ByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
METHOD (hostDeleted, "hostDeleted", "()V") \ METHOD (hostDeleted, "hostDeleted", "()V") \
CALLBACK (webViewPageLoadStarted, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \ CALLBACK (generatedCallback<&Pimpl::webViewPageLoadStarted>, "webViewPageLoadStarted", "(JLandroid/webkit/WebView;Ljava/lang/String;)Z") \
CALLBACK (webViewPageLoadFinished, "webViewPageLoadFinished", "(JLandroid/webkit/WebView;Ljava/lang/String;)V") \ CALLBACK (generatedCallback<&Pimpl::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::webViewReceivedSslError>, "webViewReceivedSslError", "(JLandroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient16, "com/rmsl/juce/JuceWebView$Client", 16, JuceWebView16ByteCode, sizeof (JuceWebView16ByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient16, "com/rmsl/juce/JuceWebView$Client", 16, JuceWebView16ByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
static jboolean JNICALL webViewPageLoadStarted (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jstring url) static jboolean webViewPageLoadStarted (JNIEnv*, Pimpl& t, jstring url)
{ {
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) return t.handlePageAboutToLoad (juceString (url));
return myself->handlePageAboutToLoad (juceString (url));
return 0;
} }
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)) t.owner.pageFinishedLoading (juceString (url));
myself->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)) const auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sslError, SslError.toString));
myself->webReceivedHttpError (errorResponse); t.owner.pageLoadHadNetworkError (juceString (errorString));
}
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));
}
} }
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
CALLBACK (webViewCloseWindowRequest, "webViewCloseWindowRequest", "(JLandroid/webkit/WebView;)V") \ CALLBACK (generatedCallback<&Pimpl::webViewCloseWindowRequest>, "webViewCloseWindowRequest", "(JLandroid/webkit/WebView;)V") \
CALLBACK (webViewCreateWindowRequest, "webViewCreateWindowRequest", "(JLandroid/webkit/WebView;)V") \ CALLBACK (generatedCallback<&Pimpl::webViewCreateWindowRequest>, "webViewCreateWindowRequest", "(JLandroid/webkit/WebView;)V") \
DECLARE_JNI_CLASS (JuceWebChromeClient, "com/rmsl/juce/JuceWebView$ChromeClient") DECLARE_JNI_CLASS (JuceWebChromeClient, "com/rmsl/juce/JuceWebView$ChromeClient")
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
static void JNICALL webViewCloseWindowRequest (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/) static void webViewCloseWindowRequest (JNIEnv*, Pimpl& t, jobject)
{ {
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host)) t.owner.windowCloseRequest();
myself->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)) t.owner.newWindowAttemptingToLoad ({});
myself->owner.newWindowAttemptingToLoad ({});
} }
//============================================================================== //==============================================================================
void webReceivedHttpError (jobject errorResponse) static void webViewReceivedHttpError (JNIEnv* env, Pimpl& t, jobject errorResponse)
{ {
auto* env = getEnv();
LocalRef<jclass> responseClass (env->FindClass ("android/webkit/WebResourceResponse")); LocalRef<jclass> responseClass (env->FindClass ("android/webkit/WebResourceResponse"));
if (responseClass != nullptr) if (responseClass != nullptr)
@ -565,14 +545,14 @@ private:
{ {
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method)); auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method));
owner.pageLoadHadNetworkError (juceString (errorString)); t.owner.pageLoadHadNetworkError (juceString (errorString));
return; return;
} }
} }
// Should never get here! // Should never get here!
jassertfalse; jassertfalse;
owner.pageLoadHadNetworkError ({}); t.owner.pageLoadHadNetworkError ({});
} }
//============================================================================== //==============================================================================
@ -596,9 +576,7 @@ WebBrowserComponent::WebBrowserComponent (const Options& options)
addAndMakeVisible (browser.get()); addAndMakeVisible (browser.get());
} }
WebBrowserComponent::~WebBrowserComponent() WebBrowserComponent::~WebBrowserComponent() = default;
{
}
//============================================================================== //==============================================================================
void WebBrowserComponent::goToURL (const String& url, void WebBrowserComponent::goToURL (const String& url,
@ -728,7 +706,4 @@ bool WebBrowserComponent::areOptionsSupported (const Options& options)
return (options.getBackend() == Options::Backend::defaultBackend); return (options.getBackend() == Options::Backend::defaultBackend);
} }
WebBrowserComponent::Pimpl::JuceWebViewClient16_Class WebBrowserComponent::Pimpl::JuceWebViewClient16;
WebBrowserComponent::Pimpl::JuceWebViewClient21_Class WebBrowserComponent::Pimpl::JuceWebViewClient21;
WebBrowserComponent::Pimpl::JuceWebChromeClient_Class WebBrowserComponent::Pimpl::JuceWebChromeClient;
} // namespace juce } // namespace juce

View file

@ -103,26 +103,6 @@ static const uint8 javaJuceOpenGLView[] =
}; };
//============================================================================== //==============================================================================
struct AndroidGLCallbacks
{
static void attachedToWindow (JNIEnv*, jobject, jlong);
static void detachedFromWindow (JNIEnv*, jobject, jlong);
static void dispatchDraw (JNIEnv*, jobject, jlong, jobject);
};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \
METHOD (layout, "layout", "(IIII)V" ) \
CALLBACK (AndroidGLCallbacks::attachedToWindow, "onAttchedWindowNative", "(J)V") \
CALLBACK (AndroidGLCallbacks::detachedFromWindow, "onDetachedFromWindowNative", "(J)V") \
CALLBACK (AndroidGLCallbacks::dispatchDraw, "onDrawNative", "(JLandroid/graphics/Canvas;)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView, sizeof(javaJuceOpenGLView))
#undef JNI_CLASS_MEMBERS
//============================================================================== //==============================================================================
class OpenGLContext::NativeContext : private SurfaceHolderCallback class OpenGLContext::NativeContext : private SurfaceHolderCallback
{ {
@ -266,40 +246,46 @@ public:
Component& component; Component& component;
private: private:
#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
//============================================================================== //==============================================================================
friend struct AndroidGLCallbacks; static void attachedToWindow (JNIEnv* env, NativeContext& t)
void attachedToWindow()
{ {
auto* env = getEnv(); LocalRef<jobject> holder (env->CallObjectMethod (t.surfaceView.get(), JuceOpenGLViewSurface.getHolder));
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder)); if (t.surfaceHolderCallback == nullptr)
t.surfaceHolderCallback = GlobalRef (CreateJavaInterface (&t, "android/view/SurfaceHolder$Callback"));
if (surfaceHolderCallback == nullptr) env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, t.surfaceHolderCallback.get());
surfaceHolderCallback = GlobalRef (CreateJavaInterface (this, "android/view/SurfaceHolder$Callback"));
env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, 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, t.surfaceHolderCallback.get());
t.surfaceHolderCallback.clear();
env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, surfaceHolderCallback.get());
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) if (t.juceContext != nullptr)
juceContext->triggerRepaint(); t.juceContext->triggerRepaint();
} }
bool tryChooseConfig (const std::vector<EGLint>& optionalAttribs) bool tryChooseConfig (const std::vector<EGLint>& optionalAttribs)
@ -414,25 +400,6 @@ private:
EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY; EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
EGLDisplay OpenGLContext::NativeContext::config; EGLDisplay OpenGLContext::NativeContext::config;
//==============================================================================
void AndroidGLCallbacks::attachedToWindow (JNIEnv*, jobject /*this*/, jlong host)
{
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->attachedToWindow();
}
void AndroidGLCallbacks::detachedFromWindow (JNIEnv*, jobject /*this*/, jlong host)
{
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->detachedFromWindow();
}
void AndroidGLCallbacks::dispatchDraw (JNIEnv*, jobject /*this*/, jlong host, jobject canvas)
{
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->dispatchDraw (canvas);
}
//============================================================================== //==============================================================================
bool OpenGLHelpers::isContextActive() bool OpenGLHelpers::isContextActive()
{ {

View file

@ -143,7 +143,7 @@ inline StringArray javaListOfStringToJuceStringArray (const LocalRef<jobject>& j
} }
//============================================================================== //==============================================================================
constexpr unsigned char juceBillingClientCompiled[] constexpr uint8 juceBillingClientCompiled[]
{ {
0x1f, 0x8b, 0x08, 0x08, 0xa4, 0x53, 0xd0, 0x62, 0x04, 0x03, 0x63, 0x6c, 0x1f, 0x8b, 0x08, 0x08, 0xa4, 0x53, 0xd0, 0x62, 0x04, 0x03, 0x63, 0x6c,
0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x9d, 0x5a, 0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x9d, 0x5a,
@ -720,42 +720,17 @@ private:
METHOD (queryPurchases, "queryPurchases", "()V") \ METHOD (queryPurchases, "queryPurchases", "()V") \
METHOD (consumePurchase, "consumePurchase", "(Ljava/lang/String;Ljava/lang/String;)V") \ METHOD (consumePurchase, "consumePurchase", "(Ljava/lang/String;Ljava/lang/String;)V") \
\ \
CALLBACK (productDetailsQueryCallback, "productDetailsQueryCallback", "(JLjava/util/List;)V") \ CALLBACK (generatedCallback<&Pimpl::updateProductDetails>, "productDetailsQueryCallback", "(JLjava/util/List;)V") \
CALLBACK (purchasesListQueryCallback, "purchasesListQueryCallback", "(JLjava/util/List;)V") \ CALLBACK (generatedCallback<&Pimpl::updatePurchasesList>, "purchasesListQueryCallback", "(JLjava/util/List;)V") \
CALLBACK (purchaseCompletedCallback, "purchaseCompletedCallback", "(JLcom/android/billingclient/api/Purchase;I)V") \ CALLBACK (generatedCallback<&Pimpl::purchaseCompleted>, "purchaseCompletedCallback", "(JLcom/android/billingclient/api/Purchase;I)V") \
CALLBACK (purchaseConsumedCallback, "purchaseConsumedCallback", "(JLjava/lang/String;I)V") CALLBACK (generatedCallback<&Pimpl::purchaseConsumed>, "purchaseConsumedCallback", "(JLjava/lang/String;I)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceBillingClient, DECLARE_JNI_CLASS_WITH_BYTECODE (JuceBillingClient,
"com/rmsl/juce/JuceBillingClient", "com/rmsl/juce/JuceBillingClient",
16, 16,
juceBillingClientCompiled, juceBillingClientCompiled)
numElementsInArray (juceBillingClientCompiled))
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
static void JNICALL productDetailsQueryCallback (JNIEnv*, jobject, jlong host, jobject productDetailsList)
{
if (auto* myself = reinterpret_cast<Pimpl*> (host))
myself->updateProductDetails (productDetailsList);
}
static void JNICALL purchasesListQueryCallback (JNIEnv*, jobject, jlong host, jobject purchasesList)
{
if (auto* myself = reinterpret_cast<Pimpl*> (host))
myself->updatePurchasesList (purchasesList);
}
static void JNICALL purchaseCompletedCallback (JNIEnv*, jobject, jlong host, jobject purchase, int responseCode)
{
if (auto* myself = reinterpret_cast<Pimpl*> (host))
myself->purchaseCompleted (purchase, responseCode);
}
static void JNICALL purchaseConsumedCallback (JNIEnv*, jobject, jlong host, jstring productIdentifier, int responseCode)
{
if (auto* myself = reinterpret_cast<Pimpl*> (host))
myself->purchaseConsumed (productIdentifier, responseCode);
}
//============================================================================== //==============================================================================
bool isReady() const bool isReady() const
{ {
@ -1095,7 +1070,4 @@ void juce_handleOnResume()
}); });
} }
InAppPurchases::Pimpl::JuceBillingClient_Class InAppPurchases::Pimpl::JuceBillingClient;
} // namespace juce } // namespace juce

View file

@ -400,7 +400,7 @@ class MediaRecorderOnInfoListener : public AndroidInterfaceImplementer
public: public:
struct Owner struct Owner
{ {
virtual ~Owner() {} virtual ~Owner() = default;
virtual void onInfo (LocalRef<jobject>& mediaRecorder, int what, int extra) = 0; virtual void onInfo (LocalRef<jobject>& mediaRecorder, int what, int extra) = 0;
}; };
@ -1711,26 +1711,25 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(JZ)V") \ METHOD (constructor, "<init>", "(JZ)V") \
CALLBACK (cameraCaptureSessionCaptureCompletedCallback, "cameraCaptureSessionCaptureCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;Landroid/hardware/camera2/TotalCaptureResult;)V") \ CALLBACK (generatedCallback<&StillPictureTaker::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 (generatedCallback<&StillPictureTaker::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 (generatedCallback<&StillPictureTaker::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 (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureStartedCallback>, "cameraCaptureSessionCaptureStarted", "(JZLandroid/hardware/camera2/CameraCaptureSession;Landroid/hardware/camera2/CaptureRequest;JJ)V") \
CALLBACK (cameraCaptureSessionCaptureSequenceAbortedCallback, "cameraCaptureSessionCaptureSequenceAborted", "(JZLandroid/hardware/camera2/CameraCaptureSession;I)V") \ CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureSequenceAbortedCallback>, "cameraCaptureSessionCaptureSequenceAborted", "(JZLandroid/hardware/camera2/CameraCaptureSession;I)V") \
CALLBACK (cameraCaptureSessionCaptureSequenceCompletedCallback, "cameraCaptureSessionCaptureSequenceCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;IJ)V") CALLBACK (generatedCallback<&StillPictureTaker::cameraCaptureSessionCaptureSequenceCompletedCallback>, "cameraCaptureSessionCaptureSequenceCompleted", "(JZLandroid/hardware/camera2/CameraCaptureSession;IJ)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (CameraCaptureSessionCaptureCallback, "com/rmsl/juce/CameraCaptureSessionCaptureCallback", 21, CameraSupportByteCode, sizeof(CameraSupportByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (CameraCaptureSessionCaptureCallback, "com/rmsl/juce/CameraCaptureSessionCaptureCallback", 21, CameraSupportByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
LocalRef<jobject> createCaptureSessionCallback (bool createPreviewSession) LocalRef<jobject> createCaptureSessionCallback (bool createPreviewSession)
{ {
return LocalRef<jobject>(getEnv()->NewObject (CameraCaptureSessionCaptureCallback, return LocalRef<jobject> (getEnv()->NewObject (CameraCaptureSessionCaptureCallback,
CameraCaptureSessionCaptureCallback.constructor, CameraCaptureSessionCaptureCallback.constructor,
reinterpret_cast<jlong> (this), reinterpret_cast<jlong> (this),
createPreviewSession ? 1 : 0)); createPreviewSession ? 1 : 0));
} }
//============================================================================== //==============================================================================
enum class State enum class State
{ {
idle = 0, idle = 0,
@ -2022,71 +2021,53 @@ private:
} }
//============================================================================== //==============================================================================
static void cameraCaptureSessionCaptureCompletedCallback (JNIEnv*, jobject /*object*/, jlong host, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult) static void cameraCaptureSessionCaptureCompletedCallback (JNIEnv* env, StillPictureTaker& t, jboolean isPreview, jobject rawSession, jobject rawRequest, jobject rawResult)
{ {
if (auto* myself = reinterpret_cast<StillPictureTaker*> (host)) LocalRef<jobject> session (env->NewLocalRef (rawSession));
{ LocalRef<jobject> request (env->NewLocalRef (rawRequest));
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); LocalRef<jobject> result (env->NewLocalRef (rawResult));
LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest));
LocalRef<jobject> result (getEnv()->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 (env->NewLocalRef (rawSession));
{ LocalRef<jobject> request (env->NewLocalRef (rawRequest));
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); LocalRef<jobject> result (env->NewLocalRef (rawResult));
LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest));
LocalRef<jobject> result (getEnv()->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 (env->NewLocalRef (rawSession));
{ LocalRef<jobject> request (env->NewLocalRef (rawRequest));
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession)); LocalRef<jobject> result (env->NewLocalRef (rawResult));
LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest));
LocalRef<jobject> result (getEnv()->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 (env->NewLocalRef (rawSession));
{
LocalRef<jobject> session (getEnv()->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 (env->NewLocalRef (rawSession));
{
LocalRef<jobject> session (getEnv()->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 (env->NewLocalRef (rawSession));
{ LocalRef<jobject> request (env->NewLocalRef (rawRequest));
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
LocalRef<jobject> request (getEnv()->NewLocalRef(rawRequest));
myself->cameraCaptureSessionCaptureStarted (isPreview != 0, session, request, timestamp, frameNumber); t.cameraCaptureSessionCaptureStarted (isPreview != 0, session, request, timestamp, frameNumber);
}
} }
}; };
@ -2115,11 +2096,11 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
CALLBACK(cameraCaptureSessionActiveCallback, "cameraCaptureSessionActive", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionActiveCallback>, "cameraCaptureSessionActive", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \
CALLBACK(cameraCaptureSessionClosedCallback, "cameraCaptureSessionClosed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionClosedCallback>, "cameraCaptureSessionClosed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \
CALLBACK(cameraCaptureSessionConfigureFailedCallback, "cameraCaptureSessionConfigureFailed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionConfigureFailedCallback>, "cameraCaptureSessionConfigureFailed", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \
CALLBACK(cameraCaptureSessionConfiguredCallback, "cameraCaptureSessionConfigured", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \ CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionConfiguredCallback>, "cameraCaptureSessionConfigured", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") \
CALLBACK(cameraCaptureSessionReadyCallback, "cameraCaptureSessionReady", "(JLandroid/hardware/camera2/CameraCaptureSession;)V") CALLBACK (generatedCallback<&CaptureSession::cameraCaptureSessionReadyCallback>, "cameraCaptureSessionReady", "(JLandroid/hardware/camera2/CameraCaptureSession;)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraCaptureSessionStateCallback, "com/rmsl/juce/CameraCaptureSessionStateCallback", 21) DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraCaptureSessionStateCallback, "com/rmsl/juce/CameraCaptureSessionStateCallback", 21)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@ -2161,124 +2142,83 @@ private:
env->CallVoidMethod (captureRequestBuilder, CaptureRequestBuilder.set, jKey.get(), jValue.get()); env->CallVoidMethod (captureRequestBuilder, CaptureRequestBuilder.set, jKey.get(), jValue.get());
} }
void cameraCaptureSessionActive ([[maybe_unused]] jobject session) //==============================================================================
static void cameraCaptureSessionActiveCallback ([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] CaptureSession& t,
[[maybe_unused]] jobject rawSession)
{ {
JUCE_CAMERA_LOG ("cameraCaptureSessionActive()"); JUCE_CAMERA_LOG ("cameraCaptureSessionActive()");
} }
void cameraCaptureSessionClosed ([[maybe_unused]] jobject session) static void cameraCaptureSessionClosedCallback ([[maybe_unused]] JNIEnv* env,
CaptureSession& t,
[[maybe_unused]] jobject rawSession)
{ {
JUCE_CAMERA_LOG ("cameraCaptureSessionClosed()"); JUCE_CAMERA_LOG ("cameraCaptureSessionClosed()");
closedEvent.signal(); t.closedEvent.signal();
} }
void cameraCaptureSessionConfigureFailed ([[maybe_unused]] jobject session) static void cameraCaptureSessionConfigureFailedCallback ([[maybe_unused]] JNIEnv* env,
CaptureSession& t,
[[maybe_unused]] jobject rawSession)
{ {
JUCE_CAMERA_LOG ("cameraCaptureSessionConfigureFailed()"); JUCE_CAMERA_LOG ("cameraCaptureSessionConfigureFailed()");
MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { this }] MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { &t }]
{ {
if (weakRef != nullptr) if (weakRef != nullptr)
weakRef->configuredCallback.captureSessionConfigured (nullptr); weakRef->configuredCallback.captureSessionConfigured (nullptr);
}); });
} }
void cameraCaptureSessionConfigured (const LocalRef<jobject>& session) static void cameraCaptureSessionConfiguredCallback (JNIEnv* env, CaptureSession& t, jobject rawSession)
{ {
LocalRef<jobject> session (env->NewLocalRef (rawSession));
JUCE_CAMERA_LOG ("cameraCaptureSessionConfigured()"); JUCE_CAMERA_LOG ("cameraCaptureSessionConfigured()");
if (pendingClose.get() == 1) if (t.pendingClose.get() == 1)
{ {
// Already closing, bailout. // Already closing, bailout.
closedEvent.signal(); t.closedEvent.signal();
GlobalRef s (session); GlobalRef s (session);
MessageManager::callAsync ([s]() MessageManager::callAsync ([s]()
{ {
getEnv()->CallVoidMethod (s, CameraCaptureSession.close); getEnv()->CallVoidMethod (s, CameraCaptureSession.close);
}); });
return; return;
} }
{ {
const ScopedLock lock (captureSessionLock); const ScopedLock lock (t.captureSessionLock);
captureSession = GlobalRef (session); t.captureSession = GlobalRef (session);
} }
MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { this }] MessageManager::callAsync ([weakRef = WeakReference<CaptureSession> { &t }]
{ {
if (weakRef == nullptr) if (weakRef == nullptr)
return; return;
weakRef->stillPictureTaker.reset (new StillPictureTaker (weakRef->captureSession, weakRef->stillPictureTaker.reset (new StillPictureTaker (weakRef->captureSession,
weakRef->captureRequestBuilder, weakRef->captureRequestBuilder,
weakRef->previewCaptureRequest, weakRef->previewCaptureRequest,
weakRef->handler, weakRef->handler,
weakRef->autoFocusMode)); weakRef->autoFocusMode));
weakRef->configuredCallback.captureSessionConfigured (weakRef.get()); weakRef->configuredCallback.captureSessionConfigured (weakRef.get());
}); });
} }
void cameraCaptureSessionReady ([[maybe_unused]] const LocalRef<jobject>& session) static void cameraCaptureSessionReadyCallback ([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] CaptureSession& t,
[[maybe_unused]] jobject rawSession)
{ {
JUCE_CAMERA_LOG ("cameraCaptureSessionReady()"); JUCE_CAMERA_LOG ("cameraCaptureSessionReady()");
} }
//==============================================================================
static void cameraCaptureSessionActiveCallback (JNIEnv*, jobject, jlong host, jobject rawSession)
{
if (auto* myself = reinterpret_cast<CaptureSession*> (host))
{
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
myself->cameraCaptureSessionActive (session);
}
}
static void cameraCaptureSessionClosedCallback (JNIEnv*, jobject, jlong host, jobject rawSession)
{
if (auto* myself = reinterpret_cast<CaptureSession*> (host))
{
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
myself->cameraCaptureSessionClosed (session);
}
}
static void cameraCaptureSessionConfigureFailedCallback (JNIEnv*, jobject, jlong host, jobject rawSession)
{
if (auto* myself = reinterpret_cast<CaptureSession*> (host))
{
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
myself->cameraCaptureSessionConfigureFailed (session);
}
}
static void cameraCaptureSessionConfiguredCallback (JNIEnv*, jobject, jlong host, jobject rawSession)
{
if (auto* myself = reinterpret_cast<CaptureSession*> (host))
{
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
myself->cameraCaptureSessionConfigured (session);
}
}
static void cameraCaptureSessionReadyCallback (JNIEnv*, jobject, jlong host, jobject rawSession)
{
if (auto* myself = reinterpret_cast<CaptureSession*> (host))
{
LocalRef<jobject> session (getEnv()->NewLocalRef(rawSession));
myself->cameraCaptureSessionReady (session);
}
}
//============================================================================== //==============================================================================
friend class ScopedCameraDevice; friend class ScopedCameraDevice;
@ -2374,10 +2314,10 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
CALLBACK (cameraDeviceStateClosedCallback, "cameraDeviceStateClosed", "(JLandroid/hardware/camera2/CameraDevice;)V") \ CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateClosedCallback>, "cameraDeviceStateClosed", "(JLandroid/hardware/camera2/CameraDevice;)V") \
CALLBACK (cameraDeviceStateDisconnectedCallback, "cameraDeviceStateDisconnected", "(JLandroid/hardware/camera2/CameraDevice;)V") \ CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateDisconnectedCallback>, "cameraDeviceStateDisconnected", "(JLandroid/hardware/camera2/CameraDevice;)V") \
CALLBACK (cameraDeviceStateErrorCallback, "cameraDeviceStateError", "(JLandroid/hardware/camera2/CameraDevice;I)V") \ CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateErrorCallback>, "cameraDeviceStateError", "(JLandroid/hardware/camera2/CameraDevice;I)V") \
CALLBACK (cameraDeviceStateOpenedCallback, "cameraDeviceStateOpened", "(JLandroid/hardware/camera2/CameraDevice;)V") CALLBACK (generatedCallback<&ScopedCameraDevice::cameraDeviceStateOpenedCallback>, "cameraDeviceStateOpened", "(JLandroid/hardware/camera2/CameraDevice;)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraDeviceStateCallback, "com/rmsl/juce/CameraDeviceStateCallback", 21) DECLARE_JNI_CLASS_WITH_MIN_SDK (CameraDeviceStateCallback, "com/rmsl/juce/CameraDeviceStateCallback", 21)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@ -2390,92 +2330,66 @@ private:
} }
//============================================================================== //==============================================================================
void cameraDeviceStateClosed()
{
JUCE_CAMERA_LOG ("cameraDeviceStateClosed()");
closedEvent.signal();
}
void cameraDeviceStateDisconnected()
{
JUCE_CAMERA_LOG ("cameraDeviceStateDisconnected()");
if (pendingOpen.compareAndSetBool (0, 1))
{
openError = "Device disconnected";
notifyOpenResult();
}
MessageManager::callAsync ([this]() { close(); });
}
void cameraDeviceStateError (int errorCode)
{
String error = cameraErrorCodeToString (errorCode);
JUCE_CAMERA_LOG ("cameraDeviceStateError(), error: " + error);
if (pendingOpen.compareAndSetBool (0, 1))
{
openError = error;
notifyOpenResult();
}
fatalErrorOccurred.set (1);
MessageManager::callAsync ([this, error]()
{
owner.cameraDeviceError (error);
close();
});
}
void cameraDeviceStateOpened (const LocalRef<jobject>& cameraDeviceToUse)
{
JUCE_CAMERA_LOG ("cameraDeviceStateOpened()");
pendingOpen.set (0);
cameraDevice = GlobalRef (cameraDeviceToUse);
notifyOpenResult();
}
void notifyOpenResult() void notifyOpenResult()
{ {
MessageManager::callAsync ([this]() { owner.cameraOpenFinished (openError); }); MessageManager::callAsync ([this]() { owner.cameraOpenFinished (openError); });
} }
//============================================================================== //==============================================================================
static void JNICALL cameraDeviceStateClosedCallback (JNIEnv*, jobject, jlong host, jobject) static void cameraDeviceStateClosedCallback (JNIEnv*, ScopedCameraDevice& s, jobject)
{ {
if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) JUCE_CAMERA_LOG ("cameraDeviceStateClosed()");
myself->cameraDeviceStateClosed();
s.closedEvent.signal();
} }
static void JNICALL cameraDeviceStateDisconnectedCallback (JNIEnv*, jobject, jlong host, jobject) static void cameraDeviceStateDisconnectedCallback (JNIEnv*, ScopedCameraDevice& s, jobject)
{ {
if (auto* myself = reinterpret_cast<ScopedCameraDevice*>(host)) JUCE_CAMERA_LOG ("cameraDeviceStateDisconnected()");
myself->cameraDeviceStateDisconnected();
}
static void JNICALL cameraDeviceStateErrorCallback (JNIEnv*, jobject, jlong host, jobject, jint error) if (s.pendingOpen.compareAndSetBool (0, 1))
{
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)); s.openError = "Device disconnected";
myself->cameraDeviceStateOpened (camera); s.notifyOpenResult();
} }
MessageManager::callAsync ([&s] { s.close(); });
}
static void cameraDeviceStateErrorCallback (JNIEnv*, ScopedCameraDevice& s, jobject, jint errorCode)
{
auto error = cameraErrorCodeToString (errorCode);
JUCE_CAMERA_LOG ("cameraDeviceStateError(), error: " + error);
if (s.pendingOpen.compareAndSetBool (0, 1))
{
s.openError = error;
s.notifyOpenResult();
}
s.fatalErrorOccurred.set (1);
MessageManager::callAsync ([&s, error]()
{
s.owner.cameraDeviceError (error);
s.close();
});
}
static void cameraDeviceStateOpenedCallback (JNIEnv* env, ScopedCameraDevice& s, jobject cameraDeviceToUse)
{
JUCE_CAMERA_LOG ("cameraDeviceStateOpened()");
LocalRef<jobject> camera (env->NewLocalRef (cameraDeviceToUse));
s.pendingOpen.set (0);
s.cameraDevice = GlobalRef (camera);
s.notifyOpenResult();
} }
}; };
@ -2815,7 +2729,7 @@ private:
METHOD (constructor, "<init>", "(JLandroid/content/Context;I)V") \ METHOD (constructor, "<init>", "(JLandroid/content/Context;I)V") \
METHOD (disable, "disable", "()V") \ METHOD (disable, "disable", "()V") \
METHOD (enable, "enable", "()V") \ METHOD (enable, "enable", "()V") \
CALLBACK (deviceOrientationChanged, "deviceOrientationChanged", "(JI)V") CALLBACK (generatedCallback<&DeviceOrientationChangeListener::orientationChanged>, "deviceOrientationChanged", "(JI)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (OrientationEventListener, "com/rmsl/juce/JuceOrientationEventListener", 21) DECLARE_JNI_CLASS_WITH_MIN_SDK (OrientationEventListener, "com/rmsl/juce/JuceOrientationEventListener", 21)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@ -2830,7 +2744,7 @@ private:
} }
//============================================================================== //==============================================================================
void orientationChanged (int orientation) static void orientationChanged (JNIEnv*, DeviceOrientationChangeListener& t, jint orientation)
{ {
jassert (orientation < 360); jassert (orientation < 360);
@ -2838,25 +2752,31 @@ private:
if (orientation < 0) if (orientation < 0)
return; return;
auto oldOrientation = deviceOrientation; const auto oldOrientation = t.deviceOrientation;
t.deviceOrientation = [orientation]
{
if (orientation > (360 - 45) || orientation < 45)
return Desktop::upright;
if (orientation < 135)
return Desktop::rotatedClockwise;
if (orientation < 225)
return Desktop::upsideDown;
return Desktop::rotatedAntiClockwise;
}();
// NB: this assumes natural position to be portrait always, but some devices may be landscape... // NB: this assumes natural position to be portrait always, but some devices may be landscape...
if (orientation > (360 - 45) || orientation < 45)
deviceOrientation = Desktop::upright;
else if (orientation < 135)
deviceOrientation = Desktop::rotatedClockwise;
else if (orientation < 225)
deviceOrientation = Desktop::upsideDown;
else
deviceOrientation = Desktop::rotatedAntiClockwise;
if (oldOrientation != deviceOrientation) if (oldOrientation != t.deviceOrientation)
{ {
lastKnownScreenOrientation = Desktop::getInstance().getCurrentOrientation(); t.lastKnownScreenOrientation = Desktop::getInstance().getCurrentOrientation();
// Need to update preview transform, but screen orientation will change slightly // Need to update preview transform, but screen orientation will change slightly
// later than sensor orientation. // later than sensor orientation.
startTimer (500); t.startTimer (500);
} }
} }
@ -2881,12 +2801,6 @@ private:
numChecksForOrientationChange = 10; numChecksForOrientationChange = 10;
} }
} }
static void deviceOrientationChanged (JNIEnv*, jobject /*obj*/, jlong host, jint orientation)
{
if (auto* myself = reinterpret_cast<DeviceOrientationChangeListener*> (host))
myself->orientationChanged (orientation);
}
}; };
//============================================================================== //==============================================================================
@ -3180,7 +3094,7 @@ private:
{ {
auto* env = getEnv(); auto* env = getEnv();
auto quitSafelyMethod = env->GetMethodID(AndroidHandlerThread, "quitSafely", "()Z"); auto quitSafelyMethod = env->GetMethodID (AndroidHandlerThread, "quitSafely", "()Z");
// this code will only run on SDK >= 21 // this code will only run on SDK >= 21
jassert(quitSafelyMethod != nullptr); jassert(quitSafelyMethod != nullptr);
@ -3268,9 +3182,3 @@ String CameraDevice::getFileExtension()
{ {
return ".mp4"; return ".mp4";
} }
//==============================================================================
CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::StillPictureTaker::CameraCaptureSessionCaptureCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::StillPictureTaker::CameraCaptureSessionCaptureCallback;
CameraDevice::Pimpl::ScopedCameraDevice::CameraDeviceStateCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CameraDeviceStateCallback;
CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::CameraCaptureSessionStateCallback_Class CameraDevice::Pimpl::ScopedCameraDevice::CaptureSession::CameraCaptureSessionStateCallback;
CameraDevice::Pimpl::DeviceOrientationChangeListener::OrientationEventListener_Class CameraDevice::Pimpl::DeviceOrientationChangeListener::OrientationEventListener;

View file

@ -32,7 +32,7 @@
// //
// files with min sdk version 21 // files with min sdk version 21
// See juce_core/native/java/README.txt on how to generate this byte-code. // See juce_core/native/java/README.txt on how to generate this byte-code.
static const unsigned char MediaSessionByteCode[] = static const uint8 MediaSessionByteCode[] =
{ 31,139,8,8,247,108,161,94,0,3,77,101,100,105,97,83,101,115,115,105,111,110,66,121,116,101,67,111,100,101,46,100,101,120,0,149, { 31,139,8,8,247,108,161,94,0,3,77,101,100,105,97,83,101,115,115,105,111,110,66,121,116,101,67,111,100,101,46,100,101,120,0,149,
152,127,108,28,71,21,199,223,236,253,180,207,190,95,254,221,186,169,211,56,137,19,234,220,145,26,226,228,28,99,199,216,196,233, 152,127,108,28,71,21,199,223,236,253,180,207,190,95,254,221,186,169,211,56,137,19,234,220,145,26,226,228,28,99,199,216,196,233,
249,71,125,182,107,76,168,187,246,109,236,77,238,118,143,221,189,171,45,132,168,170,32,21,209,63,144,74,165,170,82,81,144,64, 249,71,125,182,107,76,168,187,246,109,236,77,238,118,143,221,189,171,45,132,168,170,32,21,209,63,144,74,165,170,82,81,144,64,
@ -757,12 +757,12 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
CALLBACK (audioInfoChanged, "mediaControllerAudioInfoChanged", "(JLandroid/media/session/MediaController$PlaybackInfo;)V") \ CALLBACK (generatedCallback<&Controller::audioInfoChanged>, "mediaControllerAudioInfoChanged", "(JLandroid/media/session/MediaController$PlaybackInfo;)V") \
CALLBACK (metadataChanged, "mediaControllerMetadataChanged", "(JLandroid/media/MediaMetadata;)V") \ CALLBACK (generatedCallback<&Controller::metadataChanged>, "mediaControllerMetadataChanged", "(JLandroid/media/MediaMetadata;)V") \
CALLBACK (playbackStateChanged, "mediaControllerPlaybackStateChanged", "(JLandroid/media/session/PlaybackState;)V") \ CALLBACK (generatedCallback<&Controller::playbackStateChanged>, "mediaControllerPlaybackStateChanged", "(JLandroid/media/session/PlaybackState;)V") \
CALLBACK (sessionDestroyed, "mediaControllerSessionDestroyed", "(J)V") CALLBACK (generatedCallback<&Controller::sessionDestroyed>, "mediaControllerSessionDestroyed", "(J)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (AndroidMediaControllerCallback, "com/rmsl/juce/MediaControllerCallback", 21, MediaSessionByteCode, sizeof (MediaSessionByteCode)) DECLARE_JNI_CLASS_WITH_BYTECODE (AndroidMediaControllerCallback, "com/rmsl/juce/MediaControllerCallback", 21, MediaSessionByteCode)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
LocalRef<jobject> createControllerCallbacks() LocalRef<jobject> createControllerCallbacks()
@ -774,32 +774,24 @@ private:
//============================================================================== //==============================================================================
// MediaSessionController callbacks // MediaSessionController callbacks
static void audioInfoChanged (JNIEnv*, jobject, jlong host, [[maybe_unused]] jobject playbackInfo) static void audioInfoChanged (JNIEnv*, [[maybe_unused]] Controller& t, [[maybe_unused]] jobject playbackInfo)
{ {
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) JUCE_VIDEO_LOG ("MediaSessionController::audioInfoChanged()");
{
JUCE_VIDEO_LOG ("MediaSessionController::audioInfoChanged()");
}
} }
static void metadataChanged (JNIEnv*, jobject, jlong host, [[maybe_unused]] jobject metadata) static void metadataChanged (JNIEnv*, [[maybe_unused]] Controller&, [[maybe_unused]] jobject metadata)
{ {
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) JUCE_VIDEO_LOG ("MediaSessionController::metadataChanged()");
{
JUCE_VIDEO_LOG ("MediaSessionController::metadataChanged()");
}
} }
static void playbackStateChanged (JNIEnv*, jobject, jlong host, jobject state) static void playbackStateChanged (JNIEnv*, Controller& t, [[maybe_unused]] jobject state)
{ {
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) t.stateChanged (state);
myself->stateChanged (state);
} }
static void sessionDestroyed (JNIEnv*, jobject, jlong host) static void sessionDestroyed (JNIEnv*, [[maybe_unused]] Controller& t)
{ {
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host)) JUCE_VIDEO_LOG ("MediaSessionController::sessionDestroyed()");
JUCE_VIDEO_LOG ("MediaSessionController::sessionDestroyed()");
} }
}; };
@ -1279,11 +1271,11 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \ METHOD (constructor, "<init>", "(J)V") \
CALLBACK (pauseCallback, "mediaSessionPause", "(J)V") \ CALLBACK (generatedCallback<&MediaSession::pauseCallback>, "mediaSessionPause", "(J)V") \
CALLBACK (playCallback, "mediaSessionPlay", "(J)V") \ CALLBACK (generatedCallback<&MediaSession::playCallback>, "mediaSessionPlay", "(J)V") \
CALLBACK (playFromMediaIdCallback, "mediaSessionPlayFromMediaId", "(JLjava/lang/String;Landroid/os/Bundle;)V") \ CALLBACK (generatedCallback<&MediaSession::playFromMediaIdCallback>, "mediaSessionPlayFromMediaId", "(JLjava/lang/String;Landroid/os/Bundle;)V") \
CALLBACK (seekToCallback, "mediaSessionSeekTo", "(JJ)V") \ CALLBACK (generatedCallback<&MediaSession::seekToCallback>, "mediaSessionSeekTo", "(JJ)V") \
CALLBACK (stopCallback, "mediaSessionStop", "(J)V") CALLBACK (generatedCallback<&MediaSession::stopCallback>, "mediaSessionStop", "(J)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSessionCallback, "com/rmsl/juce/MediaSessionCallback", 21) DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSessionCallback, "com/rmsl/juce/MediaSessionCallback", 21)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@ -1297,78 +1289,62 @@ private:
//============================================================================== //==============================================================================
// MediaSession callbacks // MediaSession callbacks
static void pauseCallback (JNIEnv*, jobject, jlong host) static void pauseCallback (JNIEnv*, MediaSession& t)
{ {
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host)) JUCE_VIDEO_LOG ("MediaSession::pauseCallback()");
{ t.player.pause();
JUCE_VIDEO_LOG ("MediaSession::pauseCallback()"); t.updatePlaybackState();
myself->player.pause(); t.abandonAudioFocus();
myself->updatePlaybackState();
myself->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()");
t.requestAudioFocus();
if (! t.hasAudioFocus)
{ {
JUCE_VIDEO_LOG ("MediaSession::playCallback()"); t.errorOccurred ("Application has been denied audio focus. Try again later.");
return;
myself->requestAudioFocus();
if (! myself->hasAudioFocus)
{
myself->errorOccurred ("Application has been denied audio focus. Try again later.");
return;
}
getEnv()->CallVoidMethod (myself->nativeMediaSession, AndroidMediaSession.setActive, true);
myself->player.play();
myself->setSpeed (myself->playSpeedMult);
myself->updatePlaybackState();
} }
env->CallVoidMethod (t.nativeMediaSession, AndroidMediaSession.setActive, true);
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))); t.player.load (LocalRef<jstring> ((jstring) env->NewLocalRef (mediaId)), LocalRef<jobject> (env->NewLocalRef (extras)));
myself->updatePlaybackState(); 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; t.pendingSeekRequest = true;
myself->player.setPlayPosition ((jint) pos); t.player.setPlayPosition ((jint) pos);
myself->updatePlaybackState(); 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(); t.player.closeVideo();
myself->updatePlaybackState(); t.updatePlaybackState();
myself->abandonAudioFocus(); t.abandonAudioFocus();
myself->owner.closeVideoFinished(); t.owner.closeVideoFinished();
}
} }
//============================================================================== //==============================================================================
@ -1673,7 +1649,7 @@ private:
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/app/Activity;J)V") \ METHOD (constructor, "<init>", "(Landroid/app/Activity;J)V") \
METHOD (setEnabled, "setEnabled", "(Z)V") \ METHOD (setEnabled, "setEnabled", "(Z)V") \
CALLBACK (systemVolumeChangedCallback, "mediaSessionSystemVolumeChanged", "(J)V") CALLBACK (generatedCallback<&SystemVolumeListener::systemVolumeChanged>, "mediaSessionSystemVolumeChanged", "(J)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (SystemVolumeObserver, "com/rmsl/juce/SystemVolumeObserver", 21) DECLARE_JNI_CLASS_WITH_MIN_SDK (SystemVolumeObserver, "com/rmsl/juce/SystemVolumeObserver", 21)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@ -1693,14 +1669,14 @@ private:
// Send first notification instantly to ensure sync. // Send first notification instantly to ensure sync.
if (shouldBeEnabled) if (shouldBeEnabled)
systemVolumeChanged(); systemVolumeChanged (getEnv(), *this);
} }
private: private:
//============================================================================== //==============================================================================
void systemVolumeChanged() static void systemVolumeChanged (JNIEnv*, SystemVolumeListener& t)
{ {
MessageManager::callAsync ([weakThis = WeakReference<SystemVolumeListener> { this }]() mutable MessageManager::callAsync ([weakThis = WeakReference<SystemVolumeListener> { &t }]
{ {
if (weakThis == nullptr) if (weakThis == nullptr)
return; return;
@ -1711,13 +1687,6 @@ private:
} }
//==============================================================================
static void systemVolumeChangedCallback (JNIEnv*, jobject, jlong host)
{
if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::SystemVolumeListener*> (host))
myself->systemVolumeChanged();
}
JUCE_DECLARE_WEAK_REFERENCEABLE (SystemVolumeListener) JUCE_DECLARE_WEAK_REFERENCEABLE (SystemVolumeListener)
}; };
@ -1829,8 +1798,3 @@ private:
//============================================================================== //==============================================================================
constexpr VideoComponent::Pimpl::MediaSession::Player::StateInfo VideoComponent::Pimpl::MediaSession::Player::stateInfos[]; constexpr VideoComponent::Pimpl::MediaSession::Player::StateInfo VideoComponent::Pimpl::MediaSession::Player::stateInfos[];
//==============================================================================
VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback_Class VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback;
VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback_Class VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback;
VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver_Class VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver;