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