Browse Source

TextInputTarget: Improve IME support on Android

v7.0.9
reuk 2 years ago
parent
commit
da38c1ed2a
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
22 changed files with 1775 additions and 907 deletions
  1. +1
    -1
      modules/juce_audio_devices/native/juce_android_Midi.cpp
  2. +15
    -0
      modules/juce_core/misc/juce_Functional.h
  3. +27
    -35
      modules/juce_core/native/juce_android_JNIHelpers.cpp
  4. +56
    -27
      modules/juce_core/native/juce_android_JNIHelpers.h
  5. +1
    -1
      modules/juce_core/native/juce_android_Network.cpp
  6. +1
    -10
      modules/juce_core/native/juce_mac_ObjCHelpers.h
  7. +22
    -0
      modules/juce_gui_basics/juce_gui_basics.cpp
  8. +0
    -1
      modules/juce_gui_basics/keyboard/juce_TextInputTarget.h
  9. +4
    -12
      modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp
  10. +411
    -76
      modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java
  11. +0
    -1
      modules/juce_gui_basics/native/java/app/com/rmsl/juce/JuceContentProviderFileObserver.java
  12. +9
    -22
      modules/juce_gui_basics/native/juce_android_ContentSharer.cpp
  13. +954
    -235
      modules/juce_gui_basics/native/juce_android_Windowing.cpp
  14. +18
    -16
      modules/juce_gui_basics/widgets/juce_TextEditor.cpp
  15. +1
    -2
      modules/juce_gui_basics/windows/juce_ComponentPeer.cpp
  16. +5
    -0
      modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp
  17. +0
    -4
      modules/juce_gui_extra/native/juce_android_PushNotifications.cpp
  18. +28
    -53
      modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp
  19. +26
    -59
      modules/juce_opengl/native/juce_OpenGL_android.h
  20. +6
    -34
      modules/juce_product_unlocking/native/juce_android_InAppPurchases.cpp
  21. +134
    -226
      modules/juce_video/native/juce_android_CameraDevice.h
  22. +56
    -92
      modules/juce_video/native/juce_android_Video.h

+ 1
- 1
modules/juce_audio_devices/native/juce_android_Midi.cpp View File

@@ -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) \


+ 15
- 0
modules/juce_core/misc/juce_Functional.h View File

@@ -113,4 +113,19 @@ Object withMember (Object copy, Member OtherObject::* member, Other&& value)
template <typename Fn> struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } };
template <typename Fn> 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

+ 27
- 35
modules/juce_core/native/juce_android_JNIHelpers.cpp View File

@@ -62,7 +62,7 @@ static const uint8 invocationHandleByteCode[] =
CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \
CALLBACK (juce_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()


+ 56
- 27
modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -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

+ 1
- 1
modules/juce_core/native/juce_android_Network.cpp View File

@@ -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
//==============================================================================


+ 1
- 10
modules/juce_core/native/juce_mac_ObjCHelpers.h View File

@@ -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...))


+ 22
- 0
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -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"


+ 0
- 1
modules/juce_gui_basics/keyboard/juce_TextInputTarget.h View File

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


+ 4
- 12
modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp View File

@@ -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();


+ 411
- 76
modules/juce_gui_basics/native/java/app/com/rmsl/juce/ComponentPeerView.java View File

@@ -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;
}


+ 0
- 1
modules/juce_gui_basics/native/java/app/com/rmsl/juce/JuceContentProviderFileObserver.java View File

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


+ 9
- 22
modules/juce_gui_basics/native/juce_android_ContentSharer.cpp View File

@@ -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

+ 954
- 235
modules/juce_gui_basics/native/juce_android_Windowing.cpp
File diff suppressed because it is too large
View File


+ 18
- 16
modules/juce_gui_basics/widgets/juce_TextEditor.cpp View File

@@ -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


+ 1
- 2
modules/juce_gui_basics/windows/juce_ComponentPeer.cpp View File

@@ -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();
}


+ 5
- 0
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp View File

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


+ 0
- 4
modules/juce_gui_extra/native/juce_android_PushNotifications.cpp View File

@@ -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
//==============================================================================


+ 28
- 53
modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp View File

@@ -29,7 +29,7 @@ namespace juce
//==============================================================================
// This byte-code is generated from native/java/com/rmsl/juce/JuceWebView.java with min sdk version 16
// 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

+ 26
- 59
modules/juce_opengl/native/juce_OpenGL_android.h View File

@@ -103,26 +103,6 @@ static const uint8 javaJuceOpenGLView[] =
};
//==============================================================================
struct AndroidGLCallbacks
{
static void attachedToWindow (JNIEnv*, jobject, jlong);
static void detachedFromWindow (JNIEnv*, jobject, jlong);
static void dispatchDraw (JNIEnv*, jobject, jlong, jobject);
};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \
METHOD (layout, "layout", "(IIII)V" ) \
CALLBACK (AndroidGLCallbacks::attachedToWindow, "onAttchedWindowNative", "(J)V") \
CALLBACK (AndroidGLCallbacks::detachedFromWindow, "onDetachedFromWindowNative", "(J)V") \
CALLBACK (AndroidGLCallbacks::dispatchDraw, "onDrawNative", "(JLandroid/graphics/Canvas;)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView, sizeof(javaJuceOpenGLView))
#undef JNI_CLASS_MEMBERS
//==============================================================================
class OpenGLContext::NativeContext : private SurfaceHolderCallback
{
@@ -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()
{


+ 6
- 34
modules/juce_product_unlocking/native/juce_android_InAppPurchases.cpp View File

@@ -143,7 +143,7 @@ inline StringArray javaListOfStringToJuceStringArray (const LocalRef<jobject>& j
}
//==============================================================================
constexpr unsigned char juceBillingClientCompiled[]
constexpr uint8 juceBillingClientCompiled[]
{
0x1f, 0x8b, 0x08, 0x08, 0xa4, 0x53, 0xd0, 0x62, 0x04, 0x03, 0x63, 0x6c,
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

+ 134
- 226
modules/juce_video/native/juce_android_CameraDevice.h View File

@@ -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;

+ 56
- 92
modules/juce_video/native/juce_android_Video.h View File

@@ -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;

Loading…
Cancel
Save