Browse Source

Re-structured the low-level Android native code

Please see the forum post "Re-structuring of JUCE’s low-level Android code" and the BREAKING-CHANGES.txt for more information.
tags/2021-05-28
hogliux 7 years ago
parent
commit
008b7a9ab2
75 changed files with 7774 additions and 7338 deletions
  1. +35
    -2
      BREAKING-CHANGES.txt
  2. +14
    -4
      examples/DemoRunner/Builds/Android/app/build.gradle
  3. +3
    -3
      examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml
  4. +3
    -1
      extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
  5. +12
    -4
      extras/AudioPerformanceTest/Builds/Android/app/build.gradle
  6. +3
    -3
      extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml
  7. +3
    -1
      extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
  8. +14
    -4
      extras/AudioPluginHost/Builds/Android/app/build.gradle
  9. +2
    -2
      extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml
  10. +3
    -1
      extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
  11. +13
    -4
      extras/NetworkGraphicsDemo/Builds/Android/app/build.gradle
  12. +3
    -3
      extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml
  13. +137
    -398
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h
  14. +6
    -3
      extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
  15. +1075
    -999
      modules/juce_audio_devices/native/java/com/roli/juce/JuceMidiSupport.java
  16. +11
    -18
      modules/juce_audio_devices/native/juce_android_Audio.cpp
  17. +373
    -36
      modules/juce_audio_devices/native/juce_android_Midi.cpp
  18. +7
    -45
      modules/juce_audio_devices/native/juce_android_Oboe.cpp
  19. +10
    -28
      modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  20. +24
    -9
      modules/juce_audio_utils/native/juce_android_BluetoothMidiDevicePairingDialogue.cpp
  21. +2
    -0
      modules/juce_core/juce_core.cpp
  22. +0
    -169
      modules/juce_core/native/java/AndroidCamera.java
  23. +0
    -85
      modules/juce_core/native/java/AndroidMidiFallback.java
  24. +0
    -12
      modules/juce_core/native/java/AndroidRuntimePermissions.java
  25. +0
    -146
      modules/juce_core/native/java/AndroidVideo.java
  26. +0
    -69
      modules/juce_core/native/java/AndroidWebView.java
  27. +0
    -971
      modules/juce_core/native/java/IInAppBillingService.java
  28. +0
    -1806
      modules/juce_core/native/java/JuceAppActivity.java
  29. +0
    -16
      modules/juce_core/native/java/JuceFirebaseInstanceIdService.java
  30. +0
    -35
      modules/juce_core/native/java/JuceFirebaseMessagingService.java
  31. +19
    -0
      modules/juce_core/native/java/README.txt
  32. +58
    -0
      modules/juce_core/native/java/com/roli/juce/FragmentOverlay.java
  33. +407
    -0
      modules/juce_core/native/java/com/roli/juce/JuceHTTPStream.java
  34. +15
    -0
      modules/juce_core/native/javacore/app/com/roli/juce/JuceApp.java
  35. +13
    -0
      modules/juce_core/native/javacore/init/com/roli/juce/Java.java
  36. +118
    -65
      modules/juce_core/native/juce_android_Files.cpp
  37. +687
    -0
      modules/juce_core/native/juce_android_JNIHelpers.cpp
  38. +354
    -215
      modules/juce_core/native/juce_android_JNIHelpers.h
  39. +168
    -6
      modules/juce_core/native/juce_android_Network.cpp
  40. +200
    -27
      modules/juce_core/native/juce_android_RuntimePermissions.cpp
  41. +17
    -344
      modules/juce_core/native/juce_android_SystemStats.cpp
  42. +318
    -0
      modules/juce_core/native/juce_android_Threads.cpp
  43. +16
    -28
      modules/juce_core/native/juce_posix_SharedCode.h
  44. +41
    -0
      modules/juce_core/threads/juce_Thread.h
  45. +1
    -1
      modules/juce_events/messages/juce_Initialisation.h
  46. +149
    -10
      modules/juce_events/native/juce_android_Messaging.cpp
  47. +178
    -30
      modules/juce_graphics/native/juce_android_Fonts.cpp
  48. +4
    -5
      modules/juce_graphics/native/juce_android_GraphicsContext.cpp
  49. +0
    -7
      modules/juce_gui_basics/filebrowser/juce_ContentSharer.h
  50. +1
    -1
      modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  51. +493
    -0
      modules/juce_gui_basics/native/java/com/roli/juce/ComponentPeerView.java
  52. +13
    -15
      modules/juce_gui_basics/native/javacore/app/com/roli/juce/JuceSharingContentProvider.java
  53. +174
    -115
      modules/juce_gui_basics/native/juce_android_ContentSharer.cpp
  54. +29
    -14
      modules/juce_gui_basics/native/juce_android_FileChooser.cpp
  55. +709
    -269
      modules/juce_gui_basics/native/juce_android_Windowing.cpp
  56. +2
    -8
      modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h
  57. +2
    -6
      modules/juce_gui_extra/misc/juce_WebBrowserComponent.h
  58. +107
    -0
      modules/juce_gui_extra/native/java/com/roli/juce/JuceWebView.java
  59. +12
    -26
      modules/juce_gui_extra/native/juce_AndroidViewComponent.cpp
  60. +210
    -232
      modules/juce_gui_extra/native/juce_android_PushNotifications.cpp
  61. +242
    -126
      modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp
  62. +56
    -0
      modules/juce_opengl/native/java/com/roli/juce/JuceOpenGLView.java
  63. +1
    -2
      modules/juce_opengl/native/juce_OpenGLExtensions.h
  64. +113
    -53
      modules/juce_opengl/native/juce_OpenGL_android.h
  65. +2
    -2
      modules/juce_opengl/opengl/juce_OpenGLContext.cpp
  66. +65
    -0
      modules/juce_video/native/java/com/roli/juce/CameraCaptureSessionCaptureCallback.java
  67. +53
    -0
      modules/juce_video/native/java/com/roli/juce/CameraCaptureSessionStateCallback.java
  68. +43
    -0
      modules/juce_video/native/java/com/roli/juce/CameraDeviceStateCallback.java
  69. +24
    -0
      modules/juce_video/native/java/com/roli/juce/JuceOrientationEventListener.java
  70. +53
    -0
      modules/juce_video/native/java/com/roli/juce/MediaControllerCallback.java
  71. +78
    -0
      modules/juce_video/native/java/com/roli/juce/MediaSessionCallback.java
  72. +37
    -0
      modules/juce_video/native/java/com/roli/juce/SystemVolumeObserver.java
  73. +403
    -437
      modules/juce_video/native/juce_android_CameraDevice.h
  74. +336
    -399
      modules/juce_video/native/juce_android_Video.h
  75. +0
    -18
      modules/juce_video/playback/juce_VideoComponent.h

+ 35
- 2
BREAKING-CHANGES.txt View File

@@ -4,6 +4,41 @@ JUCE breaking changes
Develop
=======
Change
-----
Multiple changes to low-level, non-public JNI and Android APIs.
Possible Issues
---------------
If you were using any non-public, low-level JNI macros, calling java code or recieving JNI callbacks, then your code will probably no longer work. See the forum for further details.
Workaround
----------
See the forum for further details.
Rationale
---------
See the forum for further details.


Change
-----
The minimum Android version for a JUCE app is now Android 4.1

Possible Issues
---------------
Your app may not run on very old versions of Android (less than 5% of the devices).

Workaround
----------
There is no workaround.

Rationale
---------
Less than 5% of all devices in the world run versions of Android older than Android
4.1. In the interest of keeping JUCE code clean and lean, we must depricate support
for very old Android versions from time to time.

Version 5.4.0
=============
@@ -94,7 +129,6 @@ The new createAndAddParameter method is much more flexible and enables any
parameter types derived from RangedAudioParameter to be managed by the
AudioProcessorValueTreeState.
Change
------
The Projucer's per-exporter Android SDK/NDK path options have been removed.
@@ -114,7 +148,6 @@ Rationale
Having multiple places where the paths could be set was confusing and could
cause unexpected mismatches.
Change
------
SystemStats::getDeviceDescription() will now return the device code on iOS e.g.


+ 14
- 4
examples/DemoRunner/Builds/Android/app/build.gradle View File

@@ -83,11 +83,21 @@ android {
}
}
repositories {
}
sourceSets {
main.java.srcDirs +=
["../../../../../modules/juce_audio_devices/native/java",
"../../../../../modules/juce_core/native/java",
"../../../../../modules/juce_gui_basics/native/java",
"../../../../../modules/juce_gui_extra/native/java",
"../../../../../modules/juce_opengl/native/java",
"../../../../../modules/juce_video/native/java"]
}
dependencies {
}
repositories {
}
dependencies {
}
}


+ 3
- 3
examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml View File

@@ -12,15 +12,15 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
<application android:label="@string/app_name" android:icon="@drawable/icon" android:hardwareAccelerated="false">
<activity android:name="DemoRunner" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
<activity android:name="android.app.Activity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<provider android:name="com.juce.demorunner.SharingContentProvider" android:authorities="com.juce.demorunner.sharingcontentprovider"
<provider android:name="com.roli.juce.JuceSharingContentProvider" android:authorities="com.juce.demorunner.sharingcontentprovider"
android:grantUriPermissions="true" android:exported="false"/>
</application>
</manifest>

+ 3
- 1
extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt View File

@@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni")
add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c")
set_source_files_properties("${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-gnu-statement-expression")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_audioperformancetest_AudioPerformanceTest" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/audioperformancetest/AudioPerformanceTest\"" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME=com_juce_audioperformancetest_SharingContentProvider" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH=\"com/juce/audioperformancetest/SharingContentProvider\"" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
include_directories( AFTER
"../../../JuceLibraryCode"
@@ -558,6 +558,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_core/misc/juce_Uuid.h"
"../../../../../modules/juce_core/misc/juce_WindowsRegistry.h"
"../../../../../modules/juce_core/native/juce_android_Files.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.h"
"../../../../../modules/juce_core/native/juce_android_Misc.cpp"
"../../../../../modules/juce_core/native/juce_android_Network.cpp"
@@ -1828,6 +1829,7 @@ set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.cpp
set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/misc/juce_WindowsRegistry.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Files.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Misc.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Network.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)


+ 12
- 4
extras/AudioPerformanceTest/Builds/Android/app/build.gradle View File

@@ -86,11 +86,19 @@ android {
}
}
repositories {
}
sourceSets {
main.java.srcDirs +=
["../../../../../modules/juce_audio_devices/native/java",
"../../../../../modules/juce_core/native/java",
"../../../../../modules/juce_gui_basics/native/java",
"../../../../../modules/juce_gui_extra/native/java"]
}
dependencies {
}
repositories {
}
dependencies {
}
}


+ 3
- 3
extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.0"
package="com.juce.audioperformancetest">
package="com.juce.AudioPerformanceTest">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
@@ -9,8 +9,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application android:label="@string/app_name" android:hardwareAccelerated="false">
<activity android:name="AudioPerformanceTest" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:hardwareAccelerated="false">
<activity android:name="android.app.Activity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>


+ 3
- 1
extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt View File

@@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni")
add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c")
set_source_files_properties("${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-gnu-statement-expression")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_roli_juce_pluginhost_AudioPluginHost" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/roli/juce/pluginhost/AudioPluginHost\"" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME=com_roli_juce_pluginhost_SharingContentProvider" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH=\"com/roli/juce/pluginhost/SharingContentProvider\"" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
include_directories( AFTER
"../../../../../modules/juce_audio_processors/format_types/VST3_SDK"
@@ -575,6 +575,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_core/misc/juce_Uuid.h"
"../../../../../modules/juce_core/misc/juce_WindowsRegistry.h"
"../../../../../modules/juce_core/native/juce_android_Files.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.h"
"../../../../../modules/juce_core/native/juce_android_Misc.cpp"
"../../../../../modules/juce_core/native/juce_android_Network.cpp"
@@ -1918,6 +1919,7 @@ set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.cpp
set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/misc/juce_WindowsRegistry.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Files.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Misc.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Network.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)


+ 14
- 4
extras/AudioPluginHost/Builds/Android/app/build.gradle View File

@@ -83,11 +83,21 @@ android {
}
}
repositories {
}
sourceSets {
main.java.srcDirs +=
["../../../../../modules/juce_audio_devices/native/java",
"../../../../../modules/juce_core/native/java",
"../../../../../modules/juce_gui_basics/native/java",
"../../../../../modules/juce_gui_extra/native/java",
"../../../../../modules/juce_opengl/native/java",
"../../../../../modules/juce_video/native/java"]
}
dependencies {
}
repositories {
}
dependencies {
}
}


+ 2
- 2
extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml View File

@@ -11,8 +11,8 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
<application android:label="@string/app_name" android:icon="@drawable/icon" android:hardwareAccelerated="false">
<activity android:name="AudioPluginHost" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
<activity android:name="android.app.Activity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>


+ 3
- 1
extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt View File

@@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni")
add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c")
set_source_files_properties("${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-gnu-statement-expression")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=10" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_networkgraphicsdemo_JUCENetworkGraphicsDemo" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/networkgraphicsdemo/JUCENetworkGraphicsDemo\"" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME=com_juce_networkgraphicsdemo_SharingContentProvider" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH=\"com/juce/networkgraphicsdemo/SharingContentProvider\"" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=10" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
include_directories( AFTER
"../../../JuceLibraryCode"
@@ -562,6 +562,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_core/misc/juce_Uuid.h"
"../../../../../modules/juce_core/misc/juce_WindowsRegistry.h"
"../../../../../modules/juce_core/native/juce_android_Files.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp"
"../../../../../modules/juce_core/native/juce_android_JNIHelpers.h"
"../../../../../modules/juce_core/native/juce_android_Misc.cpp"
"../../../../../modules/juce_core/native/juce_android_Network.cpp"
@@ -1907,6 +1908,7 @@ set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.cpp
set_source_files_properties("../../../../../modules/juce_core/misc/juce_Uuid.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/misc/juce_WindowsRegistry.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Files.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_JNIHelpers.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Misc.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_core/native/juce_android_Network.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)


+ 13
- 4
extras/NetworkGraphicsDemo/Builds/Android/app/build.gradle View File

@@ -83,11 +83,20 @@ android {
}
}
repositories {
}
sourceSets {
main.java.srcDirs +=
["../../../../../modules/juce_audio_devices/native/java",
"../../../../../modules/juce_core/native/java",
"../../../../../modules/juce_gui_basics/native/java",
"../../../../../modules/juce_gui_extra/native/java",
"../../../../../modules/juce_opengl/native/java"]
}
dependencies {
}
repositories {
}
dependencies {
}
}


+ 3
- 3
extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.0"
package="com.juce.networkgraphicsdemo">
package="com.juce.NetworkGraphicsDemo">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
@@ -10,8 +10,8 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<application android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:name="JUCENetworkGraphicsDemo" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation"
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon">
<activity android:name="android.app.Activity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation"
android:screenOrientation="unspecified" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>


+ 137
- 398
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h View File

@@ -55,6 +55,7 @@ public:
{
case ProjectType::Target::GUIApp:
case ProjectType::Target::StaticLibrary:
case ProjectType::Target::DynamicLibrary:
case ProjectType::Target::StandalonePlugIn:
return true;
default:
@@ -93,8 +94,8 @@ public:
}
//==============================================================================
ValueWithDefault androidJavaLibs, androidRepositories, androidDependencies, androidScreenOrientation, androidActivityClass,
androidActivitySubClassName, androidActivityBaseClassName, androidManifestCustomXmlElements, androidVersionCode,
ValueWithDefault androidJavaLibs, androidAdditionalJavaFolders, androidAdditionalResourceFolders, androidRepositories, androidDependencies, androidScreenOrientation,
androidCustomActivityClass, androidCustomApplicationClass, androidManifestCustomXmlElements, androidVersionCode,
androidMinimumSDK, androidTheme, androidSharedLibraries, androidStaticLibraries, androidExtraAssetsFolder,
androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidCameraNeeded, androidBluetoothNeeded, androidExternalReadPermission,
androidExternalWritePermission, androidInAppBillingPermission, androidVibratePermission,androidOtherPermissions,
@@ -105,15 +106,16 @@ public:
AndroidProjectExporter (Project& p, const ValueTree& t)
: ProjectExporter (p, t),
androidJavaLibs (settings, Ids::androidJavaLibs, getUndoManager()),
androidAdditionalJavaFolders (settings, Ids::androidAdditionalJavaFolders, getUndoManager()),
androidAdditionalResourceFolders (settings, Ids::androidAdditionalResourceFolders, getUndoManager()),
androidRepositories (settings, Ids::androidRepositories, getUndoManager()),
androidDependencies (settings, Ids::androidDependencies, getUndoManager()),
androidScreenOrientation (settings, Ids::androidScreenOrientation, getUndoManager(), "unspecified"),
androidActivityClass (settings, Ids::androidActivityClass, getUndoManager(), createDefaultClassName()),
androidActivitySubClassName (settings, Ids::androidActivitySubClassName, getUndoManager()),
androidActivityBaseClassName (settings, Ids::androidActivityBaseClassName, getUndoManager(), "Activity"),
androidCustomActivityClass (settings, Ids::androidCustomActivityClass, getUndoManager()),
androidCustomApplicationClass (settings, Ids::androidCustomApplicationClass, getUndoManager(), "com.roli.juce.JuceApp"),
androidManifestCustomXmlElements (settings, Ids::androidManifestCustomXmlElements, getUndoManager()),
androidVersionCode (settings, Ids::androidVersionCode, getUndoManager(), "1"),
androidMinimumSDK (settings, Ids::androidMinimumSDK, getUndoManager(), "10"),
androidMinimumSDK (settings, Ids::androidMinimumSDK, getUndoManager(), "16"),
androidTheme (settings, Ids::androidTheme, getUndoManager()),
androidSharedLibraries (settings, Ids::androidSharedLibraries, getUndoManager()),
androidStaticLibraries (settings, Ids::androidStaticLibraries, getUndoManager()),
@@ -201,15 +203,11 @@ public:
auto appFolder = targetFolder.getChildFile (isLibrary() ? "lib" : "app");
removeOldFiles (targetFolder);
if (! isLibrary())
copyJavaFiles (modules);
copyExtraResourceFiles();
writeFile (targetFolder, "settings.gradle", isLibrary() ? "include ':lib'" : "include ':app'");
writeFile (targetFolder, "build.gradle", getProjectBuildGradleFileContent());
writeFile (appFolder, "build.gradle", getAppBuildGradleFileContent());
writeFile (appFolder, "build.gradle", getAppBuildGradleFileContent (modules));
writeFile (targetFolder, "local.properties", getLocalPropertiesFileContent());
writeFile (targetFolder, "gradle/wrapper/gradle-wrapper.properties", getGradleWrapperPropertiesFileContent());
@@ -583,7 +581,7 @@ private:
}
//==============================================================================
String getAppBuildGradleFileContent() const
String getAppBuildGradleFileContent (const OwnedArray<LibraryModule>& modules) const
{
MemoryOutputStream mo;
mo << "apply plugin: 'com.android." << (isLibrary() ? "library" : "application") << "'" << newLine << newLine;
@@ -603,6 +601,7 @@ private:
mo << getAndroidProductFlavours() << newLine;
mo << getAndroidVariantFilter() << newLine;
mo << getAndroidJavaSourceSets (modules) << newLine;
mo << getAndroidRepositories() << newLine;
mo << getAndroidDependencies() << newLine;
mo << getApplyPlugins() << newLine;
@@ -769,12 +768,12 @@ private:
auto repositories = StringArray::fromLines (androidRepositories.get().toString());
mo << "repositories {" << newLine;
mo << " repositories {" << newLine;
for (auto& r : repositories)
mo << " " << r << newLine;
mo << " " << r << newLine;
mo << "}" << newLine;
mo << " }" << newLine;
return mo.toString();
}
@@ -782,21 +781,21 @@ private:
String getAndroidDependencies() const
{
MemoryOutputStream mo;
mo << "dependencies {" << newLine;
mo << " dependencies {" << newLine;
for (auto& d : StringArray::fromLines (androidDependencies.get().toString()))
mo << " " << d << newLine;
mo << " " << d << newLine;
for (auto& d : StringArray::fromLines (androidJavaLibs.get().toString()))
mo << " implementation files('libs/" << File (d).getFileName() << "')" << newLine;
mo << " implementation files('libs/" << File (d).getFileName() << "')" << newLine;
if (androidEnableRemoteNotifications.get())
{
mo << " 'com.google.firebase:firebase-core:11.4.0'" << newLine;
mo << " compile 'com.google.firebase:firebase-messaging:11.4.0'" << newLine;
mo << " 'com.google.firebase:firebase-core:11.4.0'" << newLine;
mo << " compile 'com.google.firebase:firebase-messaging:11.4.0'" << newLine;
}
mo << "}" << newLine;
mo << " }" << newLine;
return mo.toString();
}
@@ -811,6 +810,90 @@ private:
return mo.toString();
}
void addModuleJavaFolderToSourceSet(StringArray& javaSourceSets, const File& javacore) const
{
if (javacore.isDirectory())
{
auto appFolder = getTargetFolder().getChildFile ("app");
RelativePath relativePath (javacore, appFolder, RelativePath::buildTargetFolder);
javaSourceSets.add (relativePath.toUnixStyle());
}
}
String getAndroidJavaSourceSets (const OwnedArray<LibraryModule>& modules) const
{
auto javaSourceSets = getSourceSetArrayFor (androidAdditionalJavaFolders.get().toString());
auto resourceSets = getSourceSetArrayFor (androidAdditionalResourceFolders.get().toString());
for (auto* module : modules)
{
auto javaFolder = module->getFolder().getChildFile ("native").getChildFile ("javacore");
addModuleJavaFolderToSourceSet (javaSourceSets, javaFolder.getChildFile("init"));
if (! isLibrary())
addModuleJavaFolderToSourceSet (javaSourceSets, javaFolder.getChildFile("app"));
}
MemoryOutputStream mo;
mo << " sourceSets {" << newLine;
mo << getSourceSetStringFor ("main.java.srcDirs", javaSourceSets);
mo << newLine;
mo << getSourceSetStringFor ("main.res.srcDirs", resourceSets);
mo << " }" << newLine;
return mo.toString();
}
StringArray getSourceSetArrayFor (const String& srcDirs) const
{
StringArray sourceSets;
for (auto folder : StringArray::fromLines (srcDirs))
{
if (File::isAbsolutePath (folder))
{
sourceSets.add (folder);
}
else
{
auto appFolder = getTargetFolder().getChildFile ("app");
auto relativePath = RelativePath (folder, RelativePath::projectFolder)
.rebased (getProject().getProjectFolder(), appFolder,
RelativePath::buildTargetFolder);
sourceSets.add (relativePath.toUnixStyle());
}
}
return sourceSets;
}
static String getSourceSetStringFor (const String& type, const StringArray& srcDirs)
{
String s;
s << " " << type << " +=" << newLine;
s << " [";
bool isFirst = true;
for (auto sourceSet : srcDirs)
{
if (! isFirst)
s << "," << newLine << " ";
isFirst = false;
s << "\"" << sourceSet << "\"";
}
s << "]" << newLine;
return s;
}
//==============================================================================
String getLocalPropertiesFileContent() const
{
@@ -835,7 +918,17 @@ private:
//==============================================================================
void createBaseExporterProperties (PropertyListBuilder& props)
{
props.add (new TextPropertyComponent (androidJavaLibs, "Java Libraries to Include", 32768, true),
props.add (new TextPropertyComponent (androidAdditionalJavaFolders, "Java Source code folders", 32768, true),
"Folders inside which additional java source files can be found (one per line). For example, if you "
"are using your own Activity you should place the java files for this into a folder and add the folder "
"path to this field.");
props.add (new TextPropertyComponent (androidAdditionalResourceFolders, "Resource folders", 32768, true),
"Folders inside which additional resource files can be found (one per line). For example, if you "
"want to add your own layout xml files then you should place a layout xml file inside a folder and add "
"the folder path to this field.");
props.add (new TextPropertyComponent (androidJavaLibs, "Java libraries to include", 32768, true),
"Java libs (JAR files) (one per line). These will be copied to app/libs folder and \"implementation files\" "
"dependency will be automatically added to module \"dependencies\" section for each library, so do "
"not add the dependency yourself.");
@@ -853,24 +946,20 @@ private:
{ "unspecified", "portrait", "landscape" }),
"The screen orientations that this app should support");
props.add (new TextPropertyComponent (androidActivityClass, "Android Activity Class Name", 256, false),
"The full java class name to use for the app's Activity class.");
props.add (new TextPropertyComponent (androidCustomActivityClass, "Custom Android Activity", 256, false),
"If not empty, specifies the Android Activity class name stored in the app's manifest which "
"should be used instead of Android's default Activity.");
props.add (new TextPropertyComponent (androidActivitySubClassName, "Android Activity Sub-Class Name", 256, false),
"If not empty, specifies the Android Activity class name stored in the app's manifest. "
"Use this if you would like to use your own Android Activity sub-class.");
props.add (new TextPropertyComponent (androidActivityBaseClassName, "Android Activity Base Class", 256, false),
"If not empty, specifies the base class to use for your activity. If custom base class is "
"specified, that base class should be a sub-class of android.app.Activity. When empty, Activity "
"(android.app.Activity) will be used as the base class. "
"Use this if you would like to use your own Android Activity base class.");
props.add (new TextPropertyComponent (androidCustomApplicationClass, "Custom Android Application", 256, false),
"If not empty, specifies the Android Application class name stored in the app's manifest which "
"should be used instead of JUCE's default JuceApp class. If you specify a custom App then you must "
"call com.roli.juce.Java.initialiseJUCE somewhere in your code before calling any JUCE functions.");
props.add (new TextPropertyComponent (androidVersionCode, "Android Version Code", 32, false),
"An integer value that represents the version of the application code, relative to other versions.");
props.add (new TextPropertyComponent (androidMinimumSDK, "Minimum SDK Version", 32, false),
"The number of the minimum version of the Android SDK that the app requires");
"The number of the minimum version of the Android SDK that the app requires (must be 16 or higher).");
props.add (new TextPropertyComponent (androidExtraAssetsFolder, "Extra Android Assets", 256, false),
"A path to a folder (relative to the project folder) which contains extra android assets.");
@@ -950,286 +1039,6 @@ private:
}
//==============================================================================
String createDefaultClassName() const
{
auto s = project.getBundleIdentifierString().toLowerCase();
if (s.length() > 5
&& s.containsChar ('.')
&& s.containsOnly ("abcdefghijklmnopqrstuvwxyz_.")
&& ! s.startsWithChar ('.'))
{
if (! s.endsWithChar ('.'))
s << ".";
}
else
{
s = "com.yourcompany.";
}
return s + CodeHelpers::makeValidIdentifier (project.getProjectFilenameRootString(), false, true, false);
}
//==============================================================================
void copyJavaFiles (const OwnedArray<LibraryModule>& modules) const
{
if (auto* coreModule = getCoreModule (modules))
{
auto package = getActivityClassPackage();
auto targetFolder = getTargetFolder();
auto inAppBillingPath = String ("com.android.vending.billing").replaceCharacter ('.', File::getSeparatorChar());
auto javaSourceFolder = coreModule->getFolder().getChildFile ("native").getChildFile ("java");
auto javaInAppBillingTarget = targetFolder.getChildFile ("app/src/main/java").getChildFile (inAppBillingPath);
auto javaTarget = targetFolder.getChildFile ("app/src/main/java")
.getChildFile (package.replaceCharacter ('.', File::getSeparatorChar()));
auto libTarget = targetFolder.getChildFile ("app/libs");
libTarget.createDirectory();
copyActivityJavaFiles (javaSourceFolder, javaTarget, package);
copyServicesJavaFiles (javaSourceFolder, javaTarget, package);
copyProviderJavaFile (javaSourceFolder, javaTarget, package);
copyAdditionalJavaFiles (javaSourceFolder, javaInAppBillingTarget);
copyAdditionalJavaLibs (libTarget);
}
}
void copyActivityJavaFiles (const File& javaSourceFolder, const File& targetFolder, const String& package) const
{
if (androidActivityClass.get().toString().contains ("_"))
throw SaveError ("Your Android activity class name or path may not contain any underscores! Try a project name without underscores.");
auto className = getActivityName();
if (className.isEmpty())
throw SaveError ("Invalid Android Activity class name: " + androidActivityClass.get().toString());
createDirectoryOrThrow (targetFolder);
auto activityCode = getActivityCode (javaSourceFolder, className, package);
auto javaDestFile = targetFolder.getChildFile (className + ".java");
overwriteFileIfDifferentOrThrow (javaDestFile, activityCode);
}
String getActivityCode (const File& javaSourceFolder, const String& className, const String& package) const
{
auto runtimePermissionsCode = getRuntimePermissionsCode (javaSourceFolder, className);
auto midiCode = getMidiCode (javaSourceFolder, className);
auto webViewCode = getWebViewCode (javaSourceFolder);
auto cameraCode = getCameraCode (javaSourceFolder);
auto videoCode = getVideoCode (javaSourceFolder);
auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java");
auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString());
{
MemoryOutputStream newFile;
for (auto& line : javaSourceLines)
{
if (line.contains ("$$JuceAndroidMidiImports$$"))
newFile << midiCode.imports;
else if (line.contains ("$$JuceAndroidMidiCode$$"))
newFile << midiCode.main;
else if (line.contains ("$$JuceAndroidRuntimePermissionsCode$$"))
newFile << runtimePermissionsCode;
else if (line.contains ("$$JuceAndroidWebViewImports$$"))
newFile << webViewCode.imports;
else if (line.contains ("$$JuceAndroidWebViewNativeCode$$"))
newFile << webViewCode.native;
else if (line.contains ("$$JuceAndroidWebViewCode$$"))
newFile << webViewCode.main;
else if (line.contains ("$$JuceAndroidCameraImports$$"))
newFile << cameraCode.imports;
else if (line.contains ("$$JuceAndroidCameraCode$$"))
newFile << cameraCode.main;
else if (line.contains ("$$JuceAndroidVideoImports$$"))
newFile << videoCode.imports;
else if (line.contains ("$$JuceAndroidVideoCode$$"))
newFile << videoCode.main;
else
newFile << line.replace ("$$JuceAppActivityBaseClass$$", androidActivityBaseClassName.get().toString())
.replace ("JuceAppActivity", className)
.replace ("package com.juce;", "package " + package + ";") << newLine;
}
javaSourceLines = StringArray::fromLines (newFile.toString());
}
while (javaSourceLines.size() > 2
&& javaSourceLines[javaSourceLines.size() - 1].trim().isEmpty()
&& javaSourceLines[javaSourceLines.size() - 2].trim().isEmpty())
javaSourceLines.remove (javaSourceLines.size() - 1);
return javaSourceLines.joinIntoString (newLine);
}
String getRuntimePermissionsCode (const File& javaSourceFolder, const String& className) const
{
if (static_cast<int> (androidMinimumSDK.get()) >= 23)
{
auto javaRuntimePermissions = javaSourceFolder.getChildFile ("AndroidRuntimePermissions.java");
return javaRuntimePermissions.loadFileAsString().replace ("JuceAppActivity", className);
}
return {};
}
struct MidiCode
{
String imports;
String main;
};
MidiCode getMidiCode (const File& javaSourceFolder, const String& className) const
{
String juceMidiCode, juceMidiImports;
juceMidiImports << newLine;
if (static_cast<int> (androidMinimumSDK.get()) >= 23)
{
auto javaAndroidMidi = javaSourceFolder.getChildFile ("AndroidMidi.java");
juceMidiImports << "import android.media.midi.*;" << newLine
<< "import android.bluetooth.*;" << newLine
<< "import android.bluetooth.le.*;" << newLine;
juceMidiCode = javaAndroidMidi.loadFileAsString().replace ("JuceAppActivity", className);
}
else
{
juceMidiCode = javaSourceFolder.getChildFile ("AndroidMidiFallback.java")
.loadFileAsString()
.replace ("JuceAppActivity", className);
}
return { juceMidiImports, juceMidiCode };
}
struct WebViewCode
{
String imports;
String native;
String main;
};
WebViewCode getWebViewCode (const File& javaSourceFolder) const
{
String juceWebViewImports, juceWebViewCodeNative, juceWebViewCode;
if (static_cast<int> (androidMinimumSDK.get()) >= 23)
juceWebViewImports << "import android.webkit.WebResourceError;" << newLine;
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
juceWebViewImports << "import android.webkit.WebResourceRequest;" << newLine;
if (static_cast<int> (androidMinimumSDK.get()) >= 11)
juceWebViewImports << "import android.webkit.WebResourceResponse;" << newLine;
auto javaWebViewFile = javaSourceFolder.getChildFile ("AndroidWebView.java");
auto juceWebViewCodeAll = javaWebViewFile.loadFileAsString();
if (static_cast<int> (androidMinimumSDK.get()) <= 10)
{
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi1_10", false, false)
.upToFirstOccurrenceOf ("WebViewApi1_10$$", false, false);
}
else
{
if (static_cast<int> (androidMinimumSDK.get()) >= 23)
{
juceWebViewCodeNative << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewNativeApi23", false, false)
.upToFirstOccurrenceOf ("WebViewNativeApi23$$", false, false);
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi23", false, false)
.upToFirstOccurrenceOf ("WebViewApi23$$", false, false);
}
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
{
juceWebViewCodeNative << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewNativeApi21", false, false)
.upToFirstOccurrenceOf ("WebViewNativeApi21$$", false, false);
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi21", false, false)
.upToFirstOccurrenceOf ("WebViewApi21$$", false, false);
}
else
{
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi11_20", false, false)
.upToFirstOccurrenceOf ("WebViewApi11_20$$", false, false);
}
}
return { juceWebViewImports, juceWebViewCodeNative, juceWebViewCode };
}
struct CameraCode
{
String imports;
String main;
};
CameraCode getCameraCode (const File& javaSourceFolder) const
{
String juceCameraImports, juceCameraCode;
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
{
juceCameraImports << "import android.hardware.camera2.*;" << newLine;
auto javaCameraFile = javaSourceFolder.getChildFile ("AndroidCamera.java");
auto juceCameraCodeAll = javaCameraFile.loadFileAsString();
juceCameraCode << juceCameraCodeAll.fromFirstOccurrenceOf ("$$CameraApi21", false, false)
.upToFirstOccurrenceOf ("CameraApi21$$", false, false);
}
return { juceCameraImports, juceCameraCode };
}
struct VideoCode
{
String imports;
String main;
};
VideoCode getVideoCode (const File& javaSourceFolder) const
{
String juceVideoImports, juceVideoCode;
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
{
juceVideoImports << "import android.database.ContentObserver;" << newLine;
juceVideoImports << "import android.media.session.*;" << newLine;
juceVideoImports << "import android.media.MediaMetadata;" << newLine;
auto javaVideoFile = javaSourceFolder.getChildFile ("AndroidVideo.java");
auto juceVideoCodeAll = javaVideoFile.loadFileAsString();
juceVideoCode << juceVideoCodeAll.fromFirstOccurrenceOf ("$$VideoApi21", false, false)
.upToFirstOccurrenceOf ("VideoApi21$$", false, false);
}
return { juceVideoImports, juceVideoCode };
}
void copyAdditionalJavaFiles (const File& sourceFolder, const File& targetFolder) const
{
auto inAppBillingJavaFileName = String ("IInAppBillingService.java");
auto inAppBillingJavaSrcFile = sourceFolder.getChildFile (inAppBillingJavaFileName);
auto inAppBillingJavaDestFile = targetFolder.getChildFile (inAppBillingJavaFileName);
createDirectoryOrThrow (targetFolder);
jassert (inAppBillingJavaSrcFile.existsAsFile());
if (inAppBillingJavaSrcFile.existsAsFile())
inAppBillingJavaSrcFile.copyFileTo (inAppBillingJavaDestFile);
}
void copyAdditionalJavaLibs (const File& targetFolder) const
{
auto libPaths = StringArray::fromLines (androidJavaLibs.get().toString());
@@ -1245,58 +1054,6 @@ private:
}
}
void copyServicesJavaFiles (const File& javaSourceFolder, const File& targetFolder, const String& package) const
{
if (androidEnableRemoteNotifications.get())
{
String instanceIdFileName ("JuceFirebaseInstanceIdService.java");
String messagingFileName ("JuceFirebaseMessagingService.java");
File instanceIdFile (javaSourceFolder.getChildFile (instanceIdFileName));
File messagingFile (javaSourceFolder.getChildFile (messagingFileName));
jassert (instanceIdFile.existsAsFile());
jassert (messagingFile .existsAsFile());
Array<File> files;
files.add (instanceIdFile);
files.add (messagingFile);
for (auto& file : files)
{
auto newContent = file.loadFileAsString()
.replace ("package com.juce;", "package " + package + ";");
auto targetFile = targetFolder.getChildFile (file.getFileName());
overwriteFileIfDifferentOrThrow (targetFile, newContent);
}
}
}
void copyProviderJavaFile (const File& javaSourceFolder, const File& targetFolder, const String& package) const
{
auto providerFile = javaSourceFolder.getChildFile ("AndroidSharingContentProvider.java");
jassert (providerFile.existsAsFile());
auto targetFile = targetFolder.getChildFile ("SharingContentProvider.java");
auto fileContent = providerFile.loadFileAsString()
.replace ("package com.juce;", "package " + package + ";");
auto commonStart = fileContent.upToFirstOccurrenceOf ("$$ContentProviderApi11", false, false);
auto commonEnd = fileContent.fromFirstOccurrenceOf ("ContentProviderApi11$$", false, false);
auto middleContent = static_cast<int> (androidMinimumSDK.get()) >= 11
? fileContent.fromFirstOccurrenceOf ("$$ContentProviderApi11", false, false)
.upToFirstOccurrenceOf ("ContentProviderApi11$$", false, false)
: String();
auto newContent = commonStart;
newContent << middleContent << commonEnd;
overwriteFileIfDifferentOrThrow (targetFile, newContent);
}
void copyExtraResourceFiles() const
{
for (ConstConfigIterator config (*this); config.next();)
@@ -1346,26 +1103,23 @@ private:
}
}
String getActivityName() const
String getActivityClass() const
{
return androidActivityClass.get().toString().fromLastOccurrenceOf (".", false, false);
}
String getActivitySubClassName() const
{
auto activityPath = androidActivitySubClassName.get().toString();
auto customActivityClass = androidCustomActivityClass.get().toString();
return (activityPath.isEmpty()) ? getActivityName() : activityPath.fromLastOccurrenceOf (".", false, false);
return (customActivityClass.isEmpty()) ? "android.app.Activity" : customActivityClass;
}
String getActivityClassPackage() const
String getApplicationClass() const
{
return androidActivityClass.get().toString().upToLastOccurrenceOf (".", false, false);
auto customApplicationClass = androidCustomApplicationClass.get().toString();
return (customApplicationClass.isEmpty()) ? "com.roli.juce.JuceApp" : customApplicationClass;
}
String getJNIActivityClassName() const
{
return androidActivityClass.get().toString().replaceCharacter ('.', '/');
return getActivityClass().replaceCharacter ('.', '/');
}
static LibraryModule* getCoreModule (const OwnedArray<LibraryModule>& modules)
@@ -1572,34 +1326,17 @@ private:
defines.set ("JUCE_ANDROID", "1");
defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get());
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_'));
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\"" + getJNIActivityClassName() + "\"");
defines.set ("JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME", getSharingContentProviderClassName().replaceCharacter('.', '_'));
defines.set ("JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH", "\"" + getSharingContentProviderClassName().replaceCharacter('.', '/') + "\"");
defines.set ("JUCE_PUSH_NOTIFICATIONS", "1");
if (androidInAppBillingPermission.get())
defines.set ("JUCE_IN_APP_PURCHASES", "1");
if (androidEnableRemoteNotifications.get())
{
auto instanceIdClassName = getActivityClassPackage() + ".JuceFirebaseInstanceIdService";
auto messagingClassName = getActivityClassPackage() + ".JuceFirebaseMessagingService";
defines.set ("JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME", instanceIdClassName.replaceCharacter ('.', '_'));
defines.set ("JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME", messagingClassName.replaceCharacter ('.', '_'));
}
if (supportsGLv3())
defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1");
return defines;
}
String getSharingContentProviderClassName() const
{
return getActivityClassPackage() + ".SharingContentProvider";
}
StringPairArray getProjectPreprocessorDefs() const
{
auto defines = getAndroidPreprocessorDefs();
@@ -1737,7 +1474,7 @@ private:
setAttributeIfNotPresent (*manifest, "xmlns:android", "http://schemas.android.com/apk/res/android");
setAttributeIfNotPresent (*manifest, "android:versionCode", androidVersionCode.get());
setAttributeIfNotPresent (*manifest, "android:versionName", project.getVersionString());
setAttributeIfNotPresent (*manifest, "package", getActivityClassPackage());
setAttributeIfNotPresent (*manifest, "package", project.getBundleIdentifierString());
return manifest;
}
@@ -1797,6 +1534,7 @@ private:
{
auto* app = getOrCreateChildWithName (manifest, "application");
setAttributeIfNotPresent (*app, "android:label", "@string/app_name");
setAttributeIfNotPresent (*app, "android:name", getApplicationClass());
if (androidTheme.get().toString().isNotEmpty())
setAttributeIfNotPresent (*app, "android:theme", androidTheme.get());
@@ -1826,7 +1564,7 @@ private:
{
auto* act = getOrCreateChildWithName (application, "activity");
setAttributeIfNotPresent (*act, "android:name", getActivitySubClassName());
setAttributeIfNotPresent (*act, "android:name", getActivityClass());
setAttributeIfNotPresent (*act, "android:label", "@string/app_name");
if (! act->hasAttribute ("android:configChanges"))
@@ -1918,8 +1656,9 @@ private:
if (androidEnableContentSharing.get())
{
auto* provider = application.createNewChildElement ("provider");
provider->setAttribute ("android:name", getSharingContentProviderClassName());
provider->setAttribute ("android:authorities", getSharingContentProviderClassName().toLowerCase());
provider->setAttribute ("android:name", "com.roli.juce.JuceSharingContentProvider");
provider->setAttribute ("android:authorities", project.getBundleIdentifierString().toLowerCase() + ".sharingcontentprovider");
provider->setAttribute ("android:grantUriPermissions", "true");
provider->setAttribute ("android:exported", "false");
}


+ 6
- 3
extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h View File

@@ -189,15 +189,18 @@ namespace Ids
DECLARE_ID (cameraPermissionNeeded);
DECLARE_ID (cameraPermissionText);
DECLARE_ID (androidJavaLibs);
DECLARE_ID (androidAdditionalJavaFolders);
DECLARE_ID (androidAdditionalResourceFolders);
DECLARE_ID (androidRepositories);
DECLARE_ID (androidDependencies);
DECLARE_ID (androidBuildConfigRemoteNotifsConfigFile);
DECLARE_ID (androidAdditionalXmlValueResources);
DECLARE_ID (androidAdditionalDrawableResources);
DECLARE_ID (androidAdditionalRawValueResources);
DECLARE_ID (androidActivityClass);
DECLARE_ID (androidActivitySubClassName);
DECLARE_ID (androidActivityBaseClassName);
// DECLARE_ID (androidActivityClass); // DEPRECATED!
const Identifier androidCustomActivityClass ("androidActivitySubClassName"); // old name is very confusing, but we need to remain backward compatible
// DECLARE_ID (androidActivityBaseClassName); // DEPRECATED!
DECLARE_ID (androidCustomApplicationClass);
DECLARE_ID (androidVersionCode);
DECLARE_ID (androidSDKPath);
DECLARE_ID (androidNDKPath);


modules/juce_audio_devices/native/java/com/roli/juce/JuceMidiSupport.java
File diff suppressed because it is too large
View File


+ 11
- 18
modules/juce_audio_devices/native/juce_android_Audio.cpp View File

@@ -23,7 +23,7 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \
METHOD (constructor, "<init>", "(IIIIII)V") \
@@ -38,7 +38,7 @@ DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
METHOD (constructor, "<init>", "(IIIII)V") \
METHOD (getState, "getState", "()I") \
@@ -50,13 +50,6 @@ DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack")
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICFIELD (SDK_INT, "SDK_INT", "I") \
DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION")
#undef JNI_CLASS_MEMBERS
//==============================================================================
enum
{
@@ -198,11 +191,11 @@ public:
if (numClientOutputChannels > 0)
{
numDeviceOutputChannels = 2;
outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor,
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM));
outputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioTrack, AudioTrack.constructor,
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM)));
const bool supportsUnderrunCount = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0;
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState);
@@ -232,11 +225,11 @@ public:
else
{
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor,
0 /* (default audio source) */, sampleRate,
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
ENCODING_PCM_16BIT,
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16)))));
inputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioRecord, AudioRecord.constructor,
0 /* (default audio source) */, sampleRate,
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
ENCODING_PCM_16BIT,
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16))))));
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState);
if (inputDeviceState > 0)


+ 373
- 36
modules/juce_audio_devices/native/juce_android_Midi.cpp View File

@@ -23,24 +23,346 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/JuceMidiSupport.java with min sdk version 23
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaMidiByteCode[] =
{31,139,8,8,173,175,226,91,0,3,106,117,99,101,95,97,117,100,105,111,95,100,101,118,105,99,101,115,46,100,101,120,0,149,
124,11,124,220,69,181,255,153,223,99,119,179,217,164,155,77,218,164,105,178,217,164,73,179,133,60,155,182,164,77,26,154,
164,105,155,118,251,160,217,22,105,144,186,77,182,237,150,100,55,236,110,250,0,175,148,135,180,32,42,42,143,42,92,254,40,
175,130,168,232,5,46,34,42,8,87,20,69,184,92,212,122,125,1,194,223,130,200,67,65,68,196,222,239,153,153,125,164,45,84,
203,231,187,103,126,103,206,156,153,57,115,230,204,153,223,46,25,141,238,113,183,117,44,160,7,183,254,71,91,73,96,244,
225,231,218,251,238,107,108,49,191,55,239,138,244,25,191,14,80,247,85,235,137,38,136,104,207,166,249,62,210,255,30,90,71,
116,150,80,252,1,224,25,155,104,19,232,17,7,81,0,244,117,55,209,93,76,11,137,10,64,215,151,128,223,3,121,104,216,209,76,
180,19,24,7,146,192,36,112,61,112,35,112,8,184,27,184,7,120,0,120,8,120,26,40,110,33,90,7,156,11,196,129,36,176,27,56,8,
124,23,248,30,240,3,224,151,192,107,192,123,64,105,43,209,12,160,18,168,1,234,129,83,128,22,160,3,88,4,108,1,46,5,110,7,
158,6,204,54,162,54,96,24,216,7,124,25,248,9,240,6,224,111,39,234,7,206,1,46,4,190,2,60,13,188,3,84,207,35,90,11,236,7,
238,0,126,2,188,1,56,58,136,170,128,5,192,32,240,97,32,9,252,16,120,23,104,153,79,180,21,184,10,248,13,80,191,128,232,76,
224,227,192,109,192,227,192,203,64,209,66,162,38,96,37,176,5,72,2,251,129,235,129,175,1,223,7,94,0,204,211,136,102,2,109,
192,48,144,0,46,1,14,2,119,1,15,3,63,7,142,0,127,3,90,59,137,150,2,67,192,78,224,223,128,135,128,103,128,231,129,130,69,
68,30,192,11,76,7,102,1,179,129,70,160,9,152,7,44,2,122,128,126,96,16,88,7,108,2,62,12,68,128,81,96,7,48,6,76,0,87,3,143,
0,47,1,255,0,74,23,195,55,128,38,96,1,176,24,232,7,206,0,206,1,118,2,73,224,82,224,38,224,30,224,251,192,175,128,87,129,
119,0,179,11,227,3,170,129,122,96,30,176,24,88,5,156,1,124,8,136,0,113,96,15,240,113,224,83,192,13,192,205,192,109,192,
55,128,7,128,71,129,159,0,191,6,94,6,222,1,236,110,216,0,168,2,234,129,86,96,49,176,19,72,3,215,1,247,3,223,5,158,0,126,
1,252,22,120,25,120,13,120,23,40,95,66,52,31,88,13,124,4,56,31,248,12,112,61,240,85,224,155,192,15,129,159,2,191,1,10,
176,95,188,64,121,143,218,59,61,192,89,192,94,224,11,192,189,192,19,192,207,128,183,0,227,116,162,18,224,20,96,62,208,15,
132,129,109,64,2,248,24,176,31,248,12,112,29,240,37,224,86,224,78,224,235,192,127,2,223,1,126,8,252,12,248,29,240,10,240,
22,96,44,133,31,0,51,128,90,160,21,152,15,116,3,43,129,51,128,51,129,17,96,23,112,25,240,57,224,58,224,6,224,139,192,237,
192,87,129,251,129,239,0,143,0,63,2,126,11,252,21,40,238,37,106,0,122,128,245,192,8,144,4,46,6,62,15,220,9,60,12,60,14,
28,6,94,6,172,62,34,31,16,0,130,64,15,48,4,108,1,226,192,167,128,175,1,143,3,255,11,188,8,188,6,252,13,16,253,216,87,64,
25,48,23,24,0,206,1,70,129,113,96,15,112,49,112,37,112,29,240,69,224,110,224,1,224,113,224,73,224,247,192,107,192,187,
128,107,25,246,6,112,42,48,31,232,6,250,128,21,192,102,32,1,236,1,46,6,174,0,174,2,238,2,238,3,30,3,158,1,94,0,222,0,222,
6,254,1,20,32,184,86,0,1,96,14,208,6,116,1,189,192,32,16,6,62,12,164,129,79,3,119,0,119,3,211,16,115,203,128,58,96,54,80,
15,52,0,115,128,70,32,8,204,5,78,1,78,5,154,0,132,89,66,216,36,132,65,66,184,35,132,53,66,8,35,132,41,66,104,34,132,33,
66,136,33,132,13,66,104,32,108,93,194,214,35,108,15,130,123,19,92,150,224,42,132,101,33,152,151,96,18,90,166,207,135,229,
192,10,96,37,48,8,172,2,86,3,33,96,13,176,22,88,199,231,4,112,6,176,1,24,2,194,164,206,149,15,1,155,129,179,129,115,128,
45,64,4,24,1,70,129,40,112,46,112,1,240,49,224,66,96,31,112,17,112,49,112,9,41,155,100,254,121,53,125,22,147,47,209,229,
35,40,87,130,26,250,153,203,166,46,215,232,242,179,90,198,210,252,90,93,126,93,243,93,121,242,56,2,233,239,154,95,168,
249,179,128,34,110,211,164,248,197,121,125,77,203,43,251,242,228,203,180,60,151,43,242,218,86,230,245,85,165,199,198,50,
126,45,83,163,203,204,175,209,114,30,173,167,78,203,84,235,114,89,147,146,229,114,149,110,91,159,215,182,65,183,229,126,
216,135,130,122,12,45,121,227,108,205,27,91,91,222,216,184,220,214,164,242,2,46,119,54,229,248,25,123,182,231,233,105,
207,27,63,151,151,230,149,51,242,157,121,250,217,15,87,234,126,23,107,62,251,194,18,93,30,211,101,214,57,174,203,235,81,
142,235,242,135,80,78,232,242,104,147,202,105,184,156,70,121,183,46,127,20,229,243,116,249,0,202,73,93,190,10,229,73,93,
190,1,229,93,186,124,75,94,249,238,60,157,15,230,149,61,121,229,71,243,202,63,206,235,247,153,60,254,179,121,229,35,121,
253,190,158,199,255,107,94,91,222,208,123,50,125,53,231,228,43,80,222,171,203,129,230,92,219,165,121,122,218,242,244,119,
230,143,225,212,92,185,169,57,215,215,124,148,211,25,61,40,159,175,203,43,155,115,182,90,143,114,74,151,207,110,86,123,
181,71,175,209,71,117,153,215,232,223,116,57,157,87,110,203,43,103,124,160,87,183,229,114,95,158,63,244,231,249,195,50,
205,159,165,203,150,244,243,54,186,143,20,93,42,184,205,52,250,132,108,219,78,159,146,244,52,250,140,164,46,234,17,236,
183,21,116,5,219,10,189,191,36,169,160,87,37,109,160,217,178,126,14,53,11,142,5,101,82,174,86,243,107,53,127,182,126,102,
122,134,224,125,101,209,181,196,212,75,127,145,84,213,215,235,250,6,61,158,6,68,219,107,36,237,163,59,37,45,167,55,37,
157,79,239,232,122,191,80,52,32,212,190,60,68,76,123,232,15,164,158,231,10,142,247,53,116,21,49,109,160,183,137,227,155,
139,190,39,169,73,143,74,106,211,255,18,199,55,39,221,40,105,29,61,168,233,255,176,205,112,82,220,160,233,87,37,181,232,
191,36,93,67,11,161,223,6,223,73,28,251,86,208,42,193,116,1,173,21,156,247,43,190,59,75,221,116,157,164,5,180,28,245,30,
173,167,72,215,23,129,115,157,164,133,180,76,40,58,32,56,46,22,209,119,137,105,45,253,146,56,118,171,241,120,17,61,127,
44,233,52,154,41,152,122,169,90,112,60,87,227,230,184,254,51,77,127,69,42,166,254,72,210,51,232,176,164,37,244,11,205,
231,250,50,173,183,12,167,83,47,244,76,215,227,42,199,105,244,152,164,173,84,46,152,46,162,10,73,187,105,158,164,93,180,
73,112,108,86,237,43,224,225,255,174,41,219,107,166,214,83,137,241,127,139,56,134,250,232,126,226,216,107,208,45,210,15,
151,201,250,26,172,163,162,130,30,150,180,129,126,40,105,152,254,91,210,229,100,74,127,61,133,74,37,61,149,202,36,93,79,
115,36,29,164,213,146,14,208,70,233,151,167,75,125,1,61,46,166,255,33,105,51,253,90,210,16,189,162,249,211,164,252,106,
154,46,233,42,234,23,138,63,168,233,26,233,207,61,82,95,173,214,87,171,245,213,106,61,181,186,93,173,110,87,171,219,213,
105,249,58,45,87,167,229,234,180,92,157,150,155,77,75,165,254,217,200,58,44,249,220,65,182,166,14,73,219,201,41,233,124,
114,105,90,160,249,94,77,75,36,109,35,159,166,51,228,190,234,149,122,235,209,255,231,37,173,163,135,36,117,208,15,72,157,
115,143,75,58,151,186,244,126,114,202,253,165,230,215,0,143,184,71,210,153,116,175,164,106,125,26,224,23,223,151,116,54,
61,33,233,70,250,137,164,97,122,82,211,167,36,45,165,167,181,220,51,146,214,211,79,37,157,69,63,151,180,147,220,178,223,
211,168,80,83,143,80,252,34,73,23,83,177,80,251,191,82,210,25,52,75,210,10,170,146,116,45,213,75,218,66,13,66,197,139,
249,146,14,80,88,198,133,38,57,159,57,200,176,190,166,227,194,111,101,60,152,11,11,40,234,148,116,58,125,91,210,74,250,
14,241,121,126,138,228,183,106,249,86,120,236,135,4,159,219,74,190,77,219,167,13,30,253,8,241,249,172,244,183,195,206,47,
19,231,141,253,82,174,3,30,206,126,63,95,183,155,15,185,253,250,249,122,253,252,255,36,157,67,127,212,207,29,66,229,156,
43,37,29,162,33,193,249,103,35,93,73,156,131,42,61,11,117,251,133,144,191,73,210,26,217,207,66,100,183,127,146,52,64,237,
66,241,89,223,105,186,221,105,186,255,211,116,63,167,233,126,78,211,253,116,98,252,191,33,166,200,136,4,231,23,106,92,
139,53,237,210,122,186,144,205,158,46,56,247,85,207,221,218,191,248,12,2,91,190,247,32,185,255,113,94,115,162,141,36,247,
189,53,42,199,18,142,92,142,196,245,46,156,105,11,215,170,231,128,110,207,252,167,78,85,180,2,116,173,174,175,213,245,
109,121,245,109,160,151,233,250,217,90,175,157,167,127,37,234,191,170,235,235,53,191,59,175,254,67,168,127,81,215,55,104,
253,211,129,195,90,255,14,80,207,58,85,63,71,183,203,31,255,221,168,63,160,235,27,243,198,151,169,127,8,245,55,233,122,
206,175,127,129,196,255,217,144,146,251,131,166,127,11,229,234,10,215,228,202,229,107,84,125,93,30,239,84,93,94,8,186,36,
175,188,114,141,202,211,89,102,8,229,115,116,219,152,166,231,107,250,9,77,111,210,244,94,77,127,156,215,199,111,53,239,
101,169,211,144,229,111,14,168,187,195,132,183,8,207,117,240,153,9,47,251,238,176,215,66,84,31,246,26,52,236,51,112,46,
177,60,235,121,124,64,229,254,97,212,156,231,189,148,248,212,139,7,118,96,141,221,50,223,183,180,220,127,15,168,123,193,
121,178,23,143,136,7,12,236,35,200,122,109,249,204,113,205,68,29,203,254,102,64,157,105,225,128,69,225,90,11,50,95,66,
141,91,204,46,89,6,221,55,99,124,30,248,224,50,41,99,203,83,30,119,121,180,153,1,154,244,222,134,62,61,34,233,189,133,
219,24,157,70,17,120,183,162,204,109,60,228,243,197,219,22,193,131,130,175,23,235,145,17,189,51,160,236,192,119,21,135,
156,25,158,151,171,187,160,175,100,94,153,77,190,218,142,178,18,140,163,4,253,121,176,111,10,41,220,206,227,226,155,145,
199,136,7,110,130,207,250,122,59,202,106,16,191,166,83,165,177,147,206,11,180,131,151,107,225,59,166,197,23,101,173,165,
109,209,141,8,90,44,231,194,125,7,150,171,187,74,190,173,122,161,101,17,250,85,250,191,161,245,251,196,52,17,110,87,150,
23,82,242,124,105,169,224,91,110,104,98,237,109,208,117,38,207,163,220,231,208,250,160,199,77,149,22,244,216,69,82,79,24,
125,199,229,133,209,35,22,137,76,157,71,215,5,255,212,89,176,128,234,12,55,60,129,109,86,105,89,232,175,141,173,108,197,
3,94,196,226,58,179,8,117,62,88,46,30,40,67,6,204,252,233,184,181,122,44,95,3,151,194,52,59,189,20,61,76,67,107,143,189,
198,182,28,231,121,175,83,237,189,165,104,229,177,227,75,11,169,247,227,193,111,199,3,30,220,104,131,223,164,172,127,141,
46,87,247,204,169,254,245,111,240,175,98,228,97,14,57,163,241,229,234,110,57,225,109,65,155,225,217,5,52,92,239,160,225,
6,55,109,158,227,130,229,207,14,56,229,218,218,210,191,4,93,180,92,197,16,159,25,238,117,80,167,112,18,211,184,247,84,
212,133,123,11,192,41,144,52,220,231,70,95,31,133,157,135,251,161,179,223,1,45,69,122,5,124,122,5,130,71,84,28,98,221,66,
52,227,248,18,114,76,215,162,15,142,153,113,126,193,69,9,239,229,218,191,212,46,35,186,113,185,218,135,62,88,69,104,222,
45,203,115,126,88,140,185,241,93,251,206,229,106,223,44,41,244,208,208,69,46,114,238,115,126,78,220,34,238,181,190,191,
203,181,84,203,90,250,182,254,64,94,123,67,239,165,239,47,87,113,46,236,45,80,94,232,197,140,33,177,209,235,148,126,192,
207,241,64,19,246,148,207,123,182,215,57,165,237,147,31,208,182,51,219,182,153,219,82,166,45,143,133,199,112,88,175,219,
132,247,0,71,15,225,161,97,163,144,134,225,41,197,217,117,56,146,183,14,133,122,29,10,97,177,217,114,29,60,122,29,60,88,
135,162,236,58,64,79,127,225,191,176,14,239,101,215,97,229,9,215,193,94,161,215,1,30,228,212,163,47,4,175,148,231,221,
174,71,5,26,95,138,44,43,154,235,183,79,232,126,223,86,239,71,116,191,238,204,90,214,173,200,173,69,134,23,204,227,153,
50,218,17,181,172,80,239,84,134,197,52,216,138,45,54,108,20,203,248,170,222,204,44,206,107,147,89,231,101,39,224,173,95,
145,31,195,44,57,167,179,87,168,56,234,11,116,216,211,176,190,241,64,1,180,134,3,188,219,93,188,239,16,47,46,144,183,140,
156,158,115,79,160,123,247,9,120,151,156,128,247,233,99,230,199,255,174,63,1,239,214,60,158,45,45,71,244,181,21,188,179,
217,14,165,176,195,157,210,14,56,111,204,18,26,182,120,132,150,180,162,73,15,173,80,239,100,170,140,122,170,54,124,98,
184,221,135,85,253,50,106,224,143,237,94,172,215,52,73,227,240,71,161,75,28,37,88,210,139,231,18,72,184,37,141,123,43,52,
191,132,252,6,238,96,194,111,52,138,34,17,124,155,103,51,19,117,53,114,124,166,204,23,28,210,103,231,94,216,220,52,87,
151,13,250,245,10,149,59,86,153,24,139,25,222,0,221,198,108,98,26,247,206,228,88,39,226,222,58,121,10,249,230,119,244,
205,0,183,150,163,180,81,105,125,22,254,220,138,200,201,103,146,137,189,228,202,158,12,126,179,4,168,196,242,5,255,94,
132,82,163,161,206,251,57,104,217,172,125,73,32,223,205,248,239,187,43,84,125,216,235,149,107,109,104,111,19,43,51,231,
178,26,141,140,155,94,245,214,78,157,203,106,15,58,87,242,253,14,115,16,152,131,8,183,249,56,39,167,112,27,230,130,252,
142,159,195,243,160,33,144,70,196,245,11,246,127,191,104,36,213,103,169,236,171,12,251,70,157,193,190,149,234,125,166,
207,154,240,54,192,114,195,225,50,26,222,84,6,139,148,81,165,249,22,180,148,227,230,227,49,106,140,114,26,222,80,14,62,
110,154,56,87,42,13,236,40,115,187,220,201,243,176,214,117,198,66,248,192,65,142,229,27,102,226,105,62,158,62,45,159,42,
166,212,85,78,121,154,46,245,197,189,115,216,242,84,107,250,140,5,243,220,124,15,199,105,155,68,28,59,108,24,88,91,41,
211,54,151,54,88,193,31,187,244,25,115,250,74,149,43,134,71,42,208,254,115,188,51,44,206,31,44,114,155,157,102,155,204,
31,44,57,110,63,133,17,176,106,76,175,180,169,153,91,97,211,183,160,99,232,247,71,245,10,155,149,182,90,225,161,236,10,
255,226,168,94,97,51,30,248,4,206,72,214,252,228,209,18,195,103,4,255,225,200,156,117,43,213,187,234,112,95,37,244,223,
192,243,48,146,222,219,53,189,5,148,91,205,146,227,49,164,230,86,10,247,67,54,112,189,28,75,13,214,48,238,253,136,28,1,
247,50,36,229,95,56,90,34,124,34,248,15,142,51,202,123,46,92,169,222,123,87,217,13,84,109,135,211,60,235,107,120,182,214,
236,190,62,142,243,105,101,7,204,217,33,100,38,229,64,93,167,53,67,246,236,128,246,26,211,79,135,17,239,194,184,194,212,
88,202,26,124,158,175,177,12,33,68,240,247,126,187,196,40,178,252,118,163,197,126,209,34,123,109,37,151,244,19,131,238,
91,169,238,62,170,255,9,111,140,117,90,195,163,51,200,111,215,153,106,181,77,140,35,222,118,30,237,194,220,59,141,82,204,
113,2,123,128,51,139,171,112,235,58,12,7,207,212,6,223,80,61,241,28,76,158,67,47,103,137,215,194,59,61,86,141,133,44,81,
142,129,163,65,67,118,255,240,205,88,237,148,63,106,155,87,25,24,139,17,238,85,51,103,11,242,188,161,79,116,138,10,57,
111,83,90,220,47,111,156,53,66,205,89,230,92,222,26,153,115,117,156,113,228,168,223,224,88,226,163,224,123,42,154,168,
125,210,40,123,10,18,231,130,220,175,99,16,209,141,251,116,163,79,119,167,183,140,124,115,121,54,55,20,121,176,94,107,41,
252,240,76,204,224,11,200,143,185,119,39,44,225,119,151,144,111,102,240,213,13,237,179,104,34,16,199,125,212,227,236,116,
46,162,240,46,140,197,129,168,231,232,128,84,39,124,113,67,123,53,218,206,66,46,204,118,43,64,6,31,32,231,195,214,11,187,
28,242,77,39,191,107,129,246,58,171,3,122,62,137,53,139,183,253,59,181,91,126,119,240,41,140,216,221,40,84,251,74,110,
239,234,116,189,116,180,14,103,220,196,210,21,244,80,103,240,121,191,27,51,123,144,228,157,186,31,51,226,239,56,84,246,
178,66,250,22,123,241,126,204,141,223,253,249,28,225,20,118,90,224,20,248,122,149,133,121,90,225,212,116,216,236,243,124,
2,165,148,127,155,210,202,183,203,12,144,173,109,75,127,45,151,214,182,165,63,183,42,89,248,119,169,244,58,246,239,45,
144,15,190,84,100,250,173,70,211,103,14,163,222,15,221,51,143,213,152,183,131,43,243,118,240,28,146,178,208,56,91,106,
236,64,187,79,201,118,53,102,29,228,214,178,246,223,13,239,154,9,185,19,69,131,186,140,46,229,11,166,160,78,68,3,166,53,
166,37,79,29,51,239,201,33,159,50,17,163,130,117,255,82,233,253,12,202,156,141,112,214,196,247,232,185,176,223,124,82,
119,32,129,155,165,186,11,24,244,237,65,229,159,190,64,149,128,21,225,109,87,242,72,140,69,70,0,43,184,13,235,192,235,
140,27,138,215,207,39,223,188,233,210,115,250,228,205,199,137,88,29,252,139,138,216,19,129,237,90,214,98,238,31,252,194,
39,115,38,142,73,179,208,99,128,248,62,207,125,87,203,236,129,253,246,233,65,149,87,251,106,217,59,125,38,223,99,156,114,
39,26,242,198,198,239,106,130,239,229,242,197,231,7,149,111,248,2,19,129,115,229,141,171,36,91,119,36,83,231,205,213,101,
250,121,53,211,79,201,7,247,227,211,54,249,7,228,183,18,199,179,18,218,232,170,162,78,215,90,220,111,212,110,114,33,159,
9,23,206,160,218,7,125,46,113,249,130,31,174,194,9,80,88,224,51,244,46,117,113,155,13,69,179,168,227,240,34,172,2,191,
233,246,20,213,254,198,231,90,112,164,157,150,91,69,174,184,215,207,246,117,116,236,154,131,250,106,185,234,178,205,180,
106,234,248,115,21,120,85,188,250,54,175,3,252,21,123,164,56,115,174,187,42,11,222,147,81,255,32,234,107,236,118,222,181,
182,175,49,120,207,97,151,75,4,159,58,236,42,16,226,242,224,127,250,221,149,72,84,131,127,46,114,97,191,185,56,227,219,
128,214,31,201,198,166,45,176,133,41,215,37,182,74,125,247,232,43,245,89,62,17,126,12,179,218,35,150,47,184,108,30,45,39,
167,131,71,143,243,164,160,246,11,98,96,193,173,245,180,220,40,176,121,244,56,37,10,59,126,57,147,58,158,155,78,117,246,
156,204,169,239,232,88,82,40,103,132,122,107,98,233,199,40,218,239,28,42,49,69,127,240,47,135,17,179,15,219,182,8,254,
250,176,141,128,191,41,248,164,207,12,254,89,221,221,121,28,95,90,165,214,135,207,58,206,179,124,237,29,94,19,126,27,198,
121,238,19,241,165,65,106,243,5,223,225,247,202,134,204,151,238,130,252,77,28,223,138,176,34,69,105,49,147,119,175,139,
199,225,194,56,124,78,95,1,223,202,194,158,74,220,232,246,115,236,47,102,159,125,133,220,158,69,158,115,100,47,144,243,
248,122,58,94,105,67,212,99,235,186,200,227,169,44,86,103,234,43,210,186,56,83,109,117,131,70,164,176,148,62,229,251,33,
180,237,244,116,83,142,119,61,120,30,103,141,211,202,227,221,8,94,93,97,189,228,184,176,23,92,56,99,38,206,60,135,10,107,
143,27,27,34,228,43,133,117,197,93,56,151,14,97,214,157,5,109,84,234,121,202,121,209,67,182,151,243,67,236,236,165,119,
208,119,188,254,162,226,204,120,60,172,131,119,231,67,228,113,119,186,225,145,119,33,211,122,168,6,214,116,34,110,187,
100,156,112,202,248,224,164,52,206,151,82,242,23,5,159,43,242,248,139,26,61,165,158,81,10,62,93,228,9,190,205,123,99,18,
30,193,223,101,85,235,239,84,84,222,118,80,92,216,124,163,56,40,56,199,51,100,94,221,182,90,125,231,85,229,132,205,157,
124,54,185,113,122,179,205,113,118,27,225,107,213,124,12,94,7,216,232,50,172,67,167,3,209,245,90,196,162,192,103,233,54,
60,47,114,52,208,84,185,235,33,231,113,212,56,56,234,70,228,185,63,181,254,70,212,179,134,58,151,159,38,218,230,209,33,
147,79,231,203,200,239,44,38,61,2,139,87,143,243,136,74,151,90,189,203,100,92,199,234,137,42,105,45,33,243,82,73,29,108,
177,24,86,183,211,134,111,13,169,24,218,105,186,116,84,85,209,148,163,168,155,130,79,22,57,130,79,20,57,252,206,70,135,
58,99,199,97,247,9,29,63,119,203,189,132,8,122,97,115,122,23,114,132,66,25,53,4,237,131,141,62,204,54,194,76,171,165,109,
220,228,182,249,60,61,75,190,163,89,147,141,213,97,129,222,133,69,190,50,223,244,14,36,57,62,43,124,141,58,97,108,121,86,
221,174,169,60,179,176,162,175,30,213,103,150,60,97,54,52,206,34,173,221,213,177,255,213,163,178,45,172,217,32,61,87,157,
56,182,44,171,19,7,209,187,44,248,227,78,225,210,183,22,117,99,9,95,195,107,115,53,106,253,14,228,221,182,223,209,104,
171,185,158,37,227,196,230,236,251,171,71,86,79,189,251,241,61,248,71,171,179,241,118,253,94,154,23,246,200,236,195,144,
117,63,93,173,238,156,252,190,204,103,76,108,216,75,253,94,174,47,144,251,29,247,154,213,234,183,12,136,59,6,175,141,75,
174,17,191,153,230,44,208,164,105,134,58,209,249,61,159,19,22,236,180,113,210,88,193,183,112,95,177,26,141,112,114,58,
233,86,182,207,193,187,103,131,35,156,44,3,111,134,204,100,125,211,235,28,179,225,47,67,180,203,25,95,138,19,49,234,65,
150,131,21,207,107,39,91,9,191,37,230,5,31,231,59,234,126,10,254,77,189,151,116,99,166,252,93,231,169,210,6,21,217,24,
229,8,169,123,126,38,38,53,34,38,169,59,167,178,210,180,144,178,71,216,59,83,238,126,126,127,164,252,194,162,138,144,250,
189,5,207,209,45,119,6,71,55,181,147,194,215,170,168,114,155,228,227,206,114,173,138,40,183,201,181,134,127,26,106,213,
12,185,106,134,174,191,17,245,124,226,222,33,189,25,153,172,119,43,123,138,204,94,236,236,46,163,236,46,98,207,135,44,
172,11,223,235,159,114,195,112,196,3,147,216,21,42,123,240,59,131,247,40,175,47,18,149,14,220,154,92,165,242,214,116,25,
113,188,230,179,100,25,172,178,86,223,181,207,208,241,226,76,157,255,26,52,116,97,243,166,161,236,59,154,177,80,254,59,
154,179,196,44,58,219,168,162,179,204,106,249,246,73,221,48,63,26,82,239,203,125,98,17,98,238,52,228,31,87,200,76,138,41,
124,216,236,152,247,246,81,231,250,26,228,230,27,250,170,105,3,218,118,204,123,245,232,198,190,42,218,104,86,161,124,228,
232,134,190,89,224,227,196,157,247,252,81,95,73,240,183,153,247,112,68,159,12,169,239,10,234,112,55,216,208,59,139,158,
246,237,3,173,166,82,115,31,45,104,47,149,229,187,107,39,2,31,231,24,235,189,140,87,223,216,216,139,243,30,59,197,247,
198,55,106,167,137,82,113,33,5,95,131,214,191,169,219,48,209,23,245,58,251,96,179,26,192,143,185,100,222,5,29,10,169,124,
71,205,183,88,199,82,131,190,18,210,239,161,68,238,13,169,73,37,66,191,19,69,206,244,238,209,42,163,17,247,131,173,194,
79,139,4,178,106,81,131,53,235,64,134,30,3,199,47,249,193,35,153,156,191,68,234,16,250,123,12,33,179,43,83,247,245,88,72,
125,127,82,73,234,62,108,200,222,44,249,93,109,21,242,180,106,177,21,245,139,136,51,247,6,244,49,138,182,60,19,191,228,7,
95,205,220,191,139,116,31,213,217,62,170,228,111,79,229,221,95,219,130,231,250,81,159,250,254,231,82,208,43,179,191,110,
85,255,14,234,231,140,60,255,182,233,22,240,238,58,134,63,168,249,247,31,211,254,209,99,158,159,242,169,62,51,191,41,226,
119,135,207,250,212,111,122,94,247,169,239,73,254,234,83,191,91,56,226,83,191,105,98,189,219,88,184,84,253,94,5,14,77,
222,210,169,122,171,142,121,150,223,149,104,58,67,83,254,237,140,33,105,187,236,127,26,245,200,88,199,117,85,121,115,17,
164,114,25,67,63,153,154,246,80,238,119,81,153,239,118,12,73,219,228,243,28,205,239,207,202,121,116,91,245,11,15,213,215,
233,217,246,66,219,65,97,134,140,89,220,46,99,159,204,239,171,20,207,161,121,14,201,83,101,103,86,87,129,166,94,77,125,
90,198,167,245,10,101,62,202,124,167,101,72,58,139,50,223,177,177,44,127,199,59,71,247,203,223,171,206,209,123,176,1,255,
89,154,122,117,92,88,164,219,44,210,119,19,150,235,214,123,168,71,215,157,174,199,111,101,203,114,214,65,50,131,131,184,
203,204,165,250,182,142,190,206,182,129,5,189,205,3,203,6,58,155,231,247,117,116,52,247,158,182,160,189,121,97,255,64,
199,252,129,254,249,253,167,181,193,180,200,211,186,71,198,98,241,88,186,135,28,221,138,26,61,93,100,245,116,205,221,196,
159,40,123,251,198,38,163,233,68,34,189,99,77,36,30,217,30,77,210,226,99,57,129,104,50,153,72,46,14,140,36,38,199,70,3,
241,68,58,176,61,154,14,100,165,2,161,129,64,106,36,18,143,163,237,233,255,92,219,209,232,182,200,228,88,190,142,200,104,
100,34,13,5,149,203,38,199,199,247,102,249,43,34,233,116,127,100,108,108,107,100,4,23,155,65,50,6,67,100,14,134,66,84,51,
184,46,48,176,103,36,58,145,142,37,226,129,221,59,98,99,209,192,200,88,34,21,139,111,15,76,36,146,105,106,24,92,247,126,
245,227,177,209,24,134,176,43,54,18,37,177,138,172,85,27,251,7,168,100,213,228,72,116,13,106,6,227,19,147,233,245,172,
194,151,97,173,155,76,103,120,158,12,79,62,149,101,158,134,38,39,184,215,150,157,145,93,17,18,33,50,66,131,100,134,6,229,
7,122,192,7,50,10,12,219,12,225,195,10,133,54,135,168,62,20,137,143,38,19,177,209,214,173,153,217,182,102,231,221,171,
204,209,69,179,63,72,106,153,156,67,23,213,126,144,16,155,176,139,230,158,76,36,99,229,46,106,61,169,232,142,72,50,50,
130,225,197,82,233,216,72,23,157,122,178,6,203,162,169,145,100,108,34,157,72,158,120,32,99,209,156,124,40,58,164,124,233,
196,115,135,40,215,231,70,251,62,250,88,104,121,108,12,131,172,239,155,140,141,141,178,190,19,153,105,138,232,7,138,108,
136,166,224,178,39,158,173,22,25,138,166,211,112,176,84,174,203,15,152,66,70,184,139,102,102,133,70,18,241,116,52,158,
110,237,103,186,7,157,213,100,171,198,163,163,177,72,43,187,110,43,59,92,102,233,155,62,88,96,48,190,45,81,207,174,202,
133,252,225,188,175,116,23,53,124,176,208,80,58,146,158,196,168,235,222,79,44,187,129,242,93,233,24,25,29,29,234,149,202,
220,106,158,118,178,6,235,226,170,201,186,137,104,60,58,26,130,7,70,165,175,4,78,210,240,3,230,158,219,221,249,235,127,
140,208,134,232,72,52,182,139,245,148,102,69,18,169,214,190,201,248,232,24,150,161,44,159,185,50,194,76,136,150,231,115,
215,71,146,35,209,177,141,147,177,209,46,242,101,43,38,211,177,177,214,80,98,251,113,188,245,145,88,50,175,175,44,175,
139,54,30,207,236,62,137,155,156,52,62,224,32,104,11,141,36,198,91,147,137,177,88,235,78,68,181,214,99,66,91,253,177,145,
189,139,218,79,210,226,184,136,218,69,243,254,201,38,249,107,210,244,79,182,81,210,161,147,72,231,172,146,245,193,247,61,
113,186,104,217,191,172,45,199,97,23,13,71,82,231,158,220,80,199,105,57,249,164,51,19,94,31,73,239,224,48,241,129,210,
188,89,71,35,99,187,98,231,182,34,180,38,176,129,113,40,182,14,196,245,129,216,63,22,73,97,67,251,79,32,51,200,145,88,
215,215,158,160,126,77,116,124,171,22,136,66,164,250,4,34,67,177,237,113,68,140,36,118,73,229,9,170,195,59,146,137,221,
104,58,61,196,103,103,107,44,209,154,119,112,119,81,137,98,143,69,226,219,91,245,56,74,243,88,131,136,147,210,94,190,60,
230,186,173,59,163,35,233,169,188,161,116,18,51,205,118,35,121,178,235,200,86,222,191,85,121,236,100,116,91,235,153,209,
200,185,27,162,219,162,201,104,28,73,66,245,7,213,242,230,151,213,114,55,246,38,147,145,189,28,150,50,61,77,229,242,121,
117,2,118,247,241,35,237,201,142,63,39,154,154,202,91,25,73,97,51,78,100,12,146,207,59,94,16,199,205,113,130,224,77,29,
253,32,14,193,136,60,166,167,229,113,229,116,188,199,48,186,168,227,24,78,247,73,207,206,158,169,122,101,247,37,121,140,
112,108,156,215,114,250,177,44,181,139,74,142,219,38,212,123,28,235,196,249,102,222,65,16,72,237,197,153,49,30,72,69,147,
50,1,244,29,191,97,201,147,191,187,168,33,255,180,110,233,239,13,133,250,122,251,87,111,9,159,181,126,96,203,154,222,112,
255,202,45,161,117,67,97,18,155,200,216,132,132,111,19,82,84,107,211,224,230,65,114,108,90,133,20,112,21,216,72,252,54,
33,35,180,54,113,74,104,111,146,92,112,228,7,75,135,84,37,202,54,127,174,82,4,105,228,166,205,36,144,57,66,153,129,148,
209,24,238,163,186,225,147,103,49,205,195,255,82,86,80,255,79,136,99,219,13,159,96,139,77,97,102,246,88,97,100,100,36,
154,74,45,31,139,108,79,145,27,153,226,100,100,76,166,203,206,76,150,111,70,70,71,249,105,52,9,57,242,232,222,7,227,163,
209,61,104,173,158,100,11,119,100,98,66,39,67,228,136,164,148,39,110,61,38,75,166,202,44,39,52,32,195,158,90,219,141,27,
7,151,145,111,235,113,153,101,158,134,140,35,149,229,56,217,105,167,242,228,182,232,235,66,193,214,116,175,30,181,107,
107,90,201,65,76,151,82,124,22,195,4,228,216,154,230,115,132,236,173,156,8,146,103,68,31,40,225,189,19,81,114,96,20,200,
4,168,120,100,74,30,77,246,200,88,52,146,100,146,72,69,201,137,92,48,14,27,83,161,46,72,133,46,206,16,35,177,120,74,178,
101,105,117,116,175,20,150,54,242,232,66,56,177,17,58,108,236,130,120,154,196,40,185,71,179,41,56,57,244,92,92,138,194,
70,153,210,40,21,101,74,74,65,225,104,214,1,82,153,186,140,201,220,234,81,230,41,5,163,177,36,134,136,136,13,118,44,149,
25,186,35,122,30,150,62,69,5,114,83,246,39,70,97,192,104,38,182,83,203,182,8,110,101,163,129,116,34,48,146,140,70,210,
209,192,214,201,49,125,29,84,186,3,219,146,137,241,64,198,77,92,219,98,241,200,88,236,252,40,213,162,52,154,91,168,229,
137,100,222,197,73,9,215,176,72,102,67,159,72,192,222,22,75,194,153,60,219,96,162,209,204,130,187,185,67,229,198,100,109,
103,131,23,240,167,50,134,137,72,66,110,124,100,84,84,114,89,57,235,113,151,232,89,185,186,227,195,214,116,174,156,152,
24,139,141,200,51,48,227,224,165,96,31,55,206,138,124,102,126,6,46,181,28,127,109,34,23,216,242,164,164,18,148,150,169,
155,118,102,167,20,72,150,92,254,226,108,81,45,175,59,251,156,34,39,202,210,223,230,162,176,114,114,156,35,56,246,46,142,
74,101,156,19,26,20,162,240,37,73,70,165,6,214,75,85,178,160,147,185,181,145,113,94,45,78,79,212,102,159,137,90,126,58,
206,84,41,242,31,95,37,245,100,234,107,142,175,87,89,99,70,128,117,51,247,216,145,162,106,134,174,90,150,117,97,140,74,
143,154,199,72,213,40,228,114,208,227,134,93,136,234,76,5,21,101,30,38,57,23,162,114,253,200,103,199,148,70,46,93,145,82,
45,146,137,137,104,50,29,195,104,166,225,113,67,116,60,145,142,102,2,10,24,67,242,152,210,145,76,14,76,6,143,162,29,242,
114,161,175,35,228,220,17,73,173,101,223,113,161,176,67,238,48,107,71,2,126,93,192,159,202,111,69,140,204,216,232,30,178,
99,114,24,86,140,151,197,142,201,69,47,136,101,223,118,20,198,82,89,75,241,67,191,218,196,81,88,37,150,26,24,159,72,239,
229,130,92,2,174,206,189,38,113,197,116,214,64,46,78,62,87,202,238,119,102,167,237,217,153,255,198,196,60,23,225,202,129,
15,206,71,220,99,9,68,70,37,230,28,215,155,195,226,211,135,220,227,217,229,161,146,241,227,118,80,241,248,148,213,163,
194,241,60,207,49,198,199,201,28,79,109,199,71,122,146,172,56,47,146,205,159,136,33,241,232,110,222,62,48,83,156,205,102,
38,182,238,36,71,98,219,182,20,134,227,75,196,251,34,233,145,29,185,140,37,69,229,216,158,83,194,52,158,226,219,97,148,
178,99,43,120,135,208,244,99,185,103,38,97,29,169,69,153,19,219,93,246,175,212,144,55,17,207,189,28,145,26,74,242,57,170,
117,81,66,95,122,225,192,232,185,56,49,229,14,204,125,230,63,47,139,142,69,246,130,61,45,195,102,215,218,149,47,167,226,
71,102,34,206,68,124,249,216,100,106,7,121,18,241,53,233,201,12,27,35,227,241,40,191,220,144,74,197,168,130,57,99,49,142,
2,114,92,253,137,241,9,196,107,200,162,165,76,63,100,60,207,60,41,11,194,184,200,157,226,210,94,218,153,83,203,248,132,
192,93,26,178,165,216,4,241,99,194,27,185,153,169,203,69,92,206,249,154,159,31,167,220,41,207,140,165,119,228,246,88,77,
166,62,183,121,167,10,204,204,8,28,95,85,204,85,121,175,250,10,248,89,109,88,87,34,147,26,22,100,74,8,120,24,49,159,131,
137,92,19,59,177,155,67,112,233,4,124,242,216,89,85,158,128,57,148,142,78,132,119,39,168,124,74,93,46,50,145,53,193,25,
168,147,47,113,131,216,194,5,178,160,162,201,132,206,223,84,73,70,162,130,76,41,165,152,50,125,45,202,148,84,52,144,21,
50,148,20,103,74,225,196,114,132,6,50,121,103,207,72,70,183,243,75,149,228,212,55,51,228,72,74,47,34,183,162,42,98,168,
178,202,212,102,38,113,216,71,83,233,156,159,175,79,198,18,240,147,189,220,86,186,130,51,169,55,21,24,233,93,145,49,178,
146,236,87,102,114,50,78,37,169,108,254,170,95,158,81,105,42,47,239,206,48,157,153,55,205,174,212,200,142,232,40,18,6,
114,164,162,72,56,70,201,74,177,159,85,242,167,122,197,187,35,50,26,24,92,23,200,101,28,46,174,99,235,210,52,236,247,254,
252,164,172,16,12,246,218,53,28,66,139,249,65,231,144,147,177,81,84,238,224,235,4,246,13,38,106,165,56,5,177,83,242,161,
64,18,110,72,69,170,152,78,76,200,71,71,74,157,210,86,10,28,244,156,225,23,192,105,50,139,155,222,17,131,49,248,179,190,
13,21,184,234,160,209,248,4,57,211,9,121,223,163,233,147,241,19,185,210,204,99,216,121,14,83,49,25,127,159,21,180,97,241,
73,156,24,146,172,219,70,93,226,102,225,44,54,222,160,174,61,198,167,47,236,106,166,144,184,28,12,90,46,201,126,147,174,
17,22,255,191,111,197,116,171,16,100,89,223,52,22,143,56,139,143,154,116,191,81,180,217,38,186,92,136,175,179,252,39,133,
241,121,113,191,225,44,62,55,100,210,237,194,106,62,104,211,146,61,33,7,181,92,123,62,196,118,75,117,7,164,186,150,61,1,
58,67,252,192,112,54,65,244,83,194,108,49,252,187,141,109,213,33,83,92,37,10,90,174,104,217,108,26,223,54,10,175,219,108,
154,223,49,138,87,111,94,242,200,224,122,219,176,77,186,84,72,37,215,210,189,194,122,87,92,38,190,102,60,143,199,238,102,
252,235,166,223,10,114,86,135,182,172,222,219,220,108,236,170,246,155,244,13,209,66,15,129,89,220,221,77,143,49,165,183,
229,231,27,194,250,187,184,196,184,69,252,15,6,219,124,43,253,93,152,234,25,117,79,176,196,35,155,151,208,207,50,133,3,
134,169,186,82,29,209,125,198,9,186,249,140,161,186,57,200,148,14,25,89,133,107,182,24,23,84,215,40,161,27,101,229,205,
242,243,77,195,160,119,81,223,220,221,76,151,154,198,3,226,122,238,253,98,211,228,210,19,232,139,46,201,43,191,107,160,
252,132,177,15,229,37,171,175,163,143,115,213,109,170,234,178,188,242,229,92,126,79,149,15,112,249,91,134,44,239,231,14,
100,233,162,108,233,179,166,69,119,137,219,197,183,208,239,102,158,215,245,38,198,181,164,27,11,242,128,113,122,104,243,
112,207,218,115,122,154,109,50,246,118,57,136,30,144,149,161,152,41,254,191,40,221,251,136,92,196,230,115,108,178,197,
172,154,69,244,35,174,165,39,229,231,79,165,228,254,61,254,42,250,157,201,158,85,109,220,96,117,25,47,94,208,212,252,104,
200,40,222,13,155,237,217,179,103,111,12,238,35,250,149,190,197,61,182,160,95,216,114,105,133,207,107,25,111,139,154,222,
253,249,93,61,198,61,217,6,189,164,133,102,120,77,58,36,218,32,115,147,209,112,136,43,233,57,39,247,123,192,52,126,47,
250,177,40,47,8,33,156,54,153,2,133,71,44,147,53,10,195,22,14,18,133,54,57,68,141,109,54,75,141,119,88,226,110,182,106,
172,102,216,50,191,98,44,216,140,182,47,162,173,101,26,95,54,26,155,119,25,23,236,134,122,248,94,191,131,28,134,195,196,
136,143,138,1,122,205,41,238,192,64,150,8,223,52,139,88,249,115,1,155,102,213,156,66,15,88,230,139,226,57,241,10,87,118,
155,5,95,55,68,200,52,161,169,117,191,209,208,100,132,171,109,211,46,88,224,48,29,5,59,45,231,87,208,174,101,181,233,184,
93,148,180,192,69,222,16,179,91,118,154,198,205,198,204,102,12,149,70,108,3,91,231,182,118,116,111,218,14,219,105,156,
135,133,64,75,135,195,185,211,116,29,17,211,165,148,48,29,100,120,170,33,4,17,219,85,67,239,194,254,213,195,75,134,197,
140,105,166,128,141,254,203,22,139,187,107,248,201,248,147,104,132,121,109,163,133,141,140,29,90,211,27,218,115,254,229,
54,13,119,211,95,45,54,108,245,146,157,177,133,198,158,234,253,237,157,177,150,26,250,155,45,94,100,239,48,137,77,17,162,
27,45,233,191,242,243,18,199,212,186,59,156,226,16,236,184,215,116,98,202,75,238,149,139,231,55,141,55,133,184,237,54,
211,250,178,225,195,60,239,54,4,166,107,222,105,136,157,171,77,251,46,99,94,76,184,109,187,197,97,55,240,82,96,150,150,
109,219,14,99,164,218,118,46,112,8,88,219,226,201,26,23,116,161,194,97,148,175,98,41,246,54,167,113,183,241,186,112,54,
249,77,113,135,33,16,81,208,215,109,188,94,190,157,205,7,62,201,203,37,4,175,150,177,187,186,214,216,221,180,200,54,12,
239,42,68,160,79,58,48,42,103,241,5,85,244,176,67,60,37,247,146,233,66,91,12,237,144,16,126,211,249,128,176,107,204,130,
151,196,233,207,239,245,63,98,218,28,168,86,155,214,213,98,222,109,194,101,59,155,49,150,234,221,60,200,123,204,66,248,
221,189,162,196,107,23,250,141,209,106,12,212,190,213,114,191,195,235,185,179,165,251,244,165,182,123,161,154,12,175,137,
93,176,136,231,225,112,57,10,28,133,198,100,151,93,200,242,244,174,30,3,92,22,189,63,122,224,40,111,88,251,90,163,230,22,
219,244,35,24,162,103,140,237,17,83,168,97,16,134,177,223,178,101,31,33,135,217,210,221,39,29,195,14,181,74,227,89,116,
141,154,221,146,3,22,105,199,130,215,151,172,49,133,156,149,117,159,113,250,18,63,111,28,118,131,3,162,100,154,191,14,
158,208,221,51,140,230,176,188,191,10,6,122,208,161,98,195,31,45,131,157,24,165,63,89,114,153,233,74,27,195,112,54,169,
97,24,51,118,24,227,213,75,98,70,113,19,166,115,171,240,122,101,39,45,215,183,204,164,67,82,176,56,70,111,217,210,33,54,
211,87,212,158,53,130,59,140,225,234,37,7,100,44,178,209,241,217,182,1,47,128,29,121,114,176,64,55,142,139,213,150,245,
78,110,236,231,247,217,38,124,28,134,132,15,192,243,97,181,26,83,112,36,125,194,48,239,20,95,21,87,168,224,223,66,207,27,
226,98,195,89,221,49,243,190,102,147,190,100,52,210,47,57,200,98,115,154,244,59,209,246,13,155,186,17,195,110,118,97,103,
52,211,28,118,223,207,186,196,151,224,239,8,118,251,133,105,120,154,224,42,231,46,89,189,7,222,117,169,172,41,222,223,96,
156,95,173,56,70,80,252,220,168,104,52,230,26,151,10,171,224,61,81,94,104,156,10,78,149,85,254,225,114,187,124,168,220,
165,30,237,114,81,254,17,48,154,202,11,164,104,125,129,3,178,21,185,102,51,140,38,150,19,21,243,114,188,114,165,188,65,
113,12,112,138,115,197,29,57,185,109,198,41,220,214,168,168,175,152,157,233,110,88,246,94,159,235,159,25,103,148,47,207,
48,156,229,103,130,209,3,180,65,202,157,97,178,212,202,242,141,248,92,150,97,186,48,244,161,114,51,43,171,21,56,202,141,
60,150,28,139,27,99,137,169,177,56,42,26,42,230,84,212,86,4,42,106,42,234,68,185,37,76,81,96,86,26,248,39,140,142,125,
251,172,91,230,204,23,207,204,17,226,239,192,161,70,33,126,5,28,12,10,113,23,112,104,174,16,111,2,247,240,143,174,237,
181,23,185,72,28,3,108,98,151,67,255,230,70,208,146,139,246,89,135,79,53,47,54,168,71,28,104,18,214,43,77,134,184,178,89,
136,155,128,123,128,103,129,55,129,187,91,16,241,29,133,234,215,242,104,243,74,203,74,113,85,171,176,222,108,21,226,234,
54,244,11,188,2,236,107,39,67,184,139,13,113,85,96,8,98,87,183,135,197,61,237,66,252,0,248,21,240,58,112,203,60,33,238,7,
126,12,60,11,188,9,28,232,32,75,216,94,76,80,112,211,115,208,244,134,142,45,226,209,14,33,158,154,47,196,51,11,160,29,
184,122,33,57,157,165,101,74,76,255,55,10,217,103,23,26,198,193,78,97,220,180,200,48,14,44,6,186,76,227,234,238,66,227,
192,146,168,245,66,143,41,238,233,53,196,221,125,134,120,182,15,86,3,61,216,143,33,1,119,15,96,40,203,133,56,2,252,106,
16,163,15,9,113,120,13,250,2,14,172,53,196,161,181,224,175,67,27,224,224,122,88,192,152,37,248,223,199,208,231,85,27,46,
20,247,108,16,226,170,33,254,13,158,223,45,220,23,137,3,251,172,23,134,4,42,247,133,69,193,65,224,210,141,148,251,251,69,
249,191,225,201,252,109,62,254,109,74,230,239,243,241,239,82,50,127,163,143,127,151,18,32,245,119,250,248,183,57,153,191,
213,231,160,220,223,235,51,189,234,183,52,242,119,83,1,245,183,154,234,75,33,19,80,50,252,255,180,11,175,250,253,61,255,
127,232,70,64,245,203,127,223,207,212,242,252,255,75,91,1,245,187,27,254,127,170,237,128,26,31,255,127,240,164,245,200,
31,228,121,21,159,255,174,224,255,1,51,160,178,16,144,80,0,0};
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$MidiDeviceManager;") \
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$BluetoothManager;")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/roli/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode))
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \
METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \
METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \
METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \
METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \
METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;")
DECLARE_JNI_CLASS (MidiDeviceManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager")
DECLARE_JNI_CLASS_WITH_MIN_SDK (MidiDeviceManager, "com/roli/juce/JuceMidiSupport$MidiDeviceManager", 23)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (start, "start", "()V" )\
METHOD (stop, "stop", "()V") \
METHOD (close, "close", "()V") \
METHOD (sendMidi, "sendMidi", "([BII)V")
DECLARE_JNI_CLASS (JuceMidiPort, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort")
#undef JNI_CLASS_MEMBERS
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/roli/juce/JuceMidiSupport$JuceMidiPort", 23)
#undef JNI_CLASS_MEMBERS
//==============================================================================
class AndroidMidiInput
@@ -51,10 +373,10 @@ public:
: juceMidiInput (midiInput),
callback (midiInputCallback),
midiConcatenator (2048),
javaMidiDevice (getEnv()->CallObjectMethod (deviceManager,
MidiDeviceManager.openMidiInputPortWithJuceIndex,
(jint) portIdx,
(jlong) this))
javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager,
MidiDeviceManager.openMidiInputPortWithJuceIndex,
(jint) portIdx,
(jlong) this)))
{
}
@@ -86,10 +408,12 @@ public:
callback = nullptr;
}
void receive (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
void handleMidi (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
{
auto* env = getEnv();
jassert (byteArray != nullptr);
jbyte* data = getEnv()->GetByteArrayElements (byteArray, nullptr);
jbyte* data = env->GetByteArrayElements (byteArray, nullptr);
HeapBlock<uint8> buffer (static_cast<size_t> (len));
std::memcpy (buffer.get(), data + offset, static_cast<size_t> (len));
@@ -98,7 +422,15 @@ public:
len, static_cast<double> (timestamp) * 1.0e-9,
juceMidiInput, *callback);
getEnv()->ReleaseByteArrayElements (byteArray, data, 0);
env->ReleaseByteArrayElements (byteArray, data, 0);
}
static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray,
jint offset, jint len, jlong timestamp)
{
auto* myself = reinterpret_cast<AndroidMidiInput*> (host);
myself->handleMidi (byteArray, offset, len, timestamp);
}
private:
@@ -112,7 +444,7 @@ private:
class AndroidMidiOutput
{
public:
AndroidMidiOutput (jobject midiDevice)
AndroidMidiOutput (const LocalRef<jobject>& midiDevice)
: javaMidiDevice (midiDevice)
{
}
@@ -138,24 +470,19 @@ private:
GlobalRef javaMidiDevice;
};
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceMidiInputPort), handleReceive,
void, (JNIEnv* env, jobject, jlong host, jbyteArray byteArray,
jint offset, jint count, jlong timestamp))
{
// Java may create a Midi thread which JUCE doesn't know about and this callback may be
// received on this thread. Java will have already created a JNI Env for this new thread,
// which we need to tell JUCE about
setEnv (env);
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
CALLBACK (AndroidMidiInput::handleReceive, "handleReceive", "(J[BIIJ)V" )
reinterpret_cast<AndroidMidiInput*> (host)->receive (byteArray, offset, count, timestamp);
}
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/roli/juce/JuceMidiSupport$JuceMidiInputPort", 23)
#undef JNI_CLASS_MEMBERS
//==============================================================================
class AndroidMidiDeviceManager
{
public:
AndroidMidiDeviceManager()
: deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager))
: deviceManager (LocalRef<jobject>(getEnv()->CallStaticObjectMethod (JuceMidiSupport, JuceMidiSupport.getAndroidMidiDeviceManager, getAppContext().get())))
{
}
@@ -187,7 +514,7 @@ public:
{
jobjectArray jDevices
= (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDevices
: MidiDeviceManager.getJuceAndroidMidiOutputDevices);
: MidiDeviceManager.getJuceAndroidMidiOutputDevices);
// Create a local reference as converting this
// to a JUCE string will call into JNI
@@ -215,7 +542,7 @@ public:
{
if (jobject dm = deviceManager.get())
if (jobject javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithJuceIndex, (jint) idx))
return new AndroidMidiOutput (javaMidiPort);
return new AndroidMidiOutput (LocalRef<jobject>(javaMidiPort));
return nullptr;
}
@@ -227,18 +554,23 @@ private:
//==============================================================================
StringArray MidiOutput::getDevices()
{
AndroidMidiDeviceManager manager;
return manager.getDevices (false);
if (getAndroidSDKVersion() >= 23)
{
AndroidMidiDeviceManager manager;
return manager.getDevices (false);
}
return {};
}
int MidiOutput::getDefaultDeviceIndex()
{
return 0;
return (getAndroidSDKVersion() >= 23 ? 0 : -1);
}
MidiOutput* MidiOutput::openDevice (int index)
{
if (index < 0)
if (index < 0 || getAndroidSDKVersion() < 23)
return nullptr;
AndroidMidiDeviceManager manager;
@@ -295,18 +627,23 @@ MidiInput::MidiInput (const String& nm) : name (nm)
StringArray MidiInput::getDevices()
{
AndroidMidiDeviceManager manager;
return manager.getDevices (true);
if (getAndroidSDKVersion() >= 23)
{
AndroidMidiDeviceManager manager;
return manager.getDevices (true);
}
return {};
}
int MidiInput::getDefaultDeviceIndex()
{
return 0;
return (getAndroidSDKVersion() >= 23 ? 0 : -1);
}
MidiInput* MidiInput::openDevice (int index, juce::MidiInputCallback* callback)
{
if (index < 0)
if (getAndroidSDKVersion() < 23 || index < 0)
return nullptr;
AndroidMidiDeviceManager manager;


+ 7
- 45
modules/juce_audio_devices/native/juce_android_Oboe.cpp View File

@@ -760,8 +760,6 @@ private:
oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override
{
attachAndroidJNI();
if (audioCallbackGuard.compareAndSetBool (1, 0))
{
if (stream == nullptr)
@@ -905,8 +903,6 @@ private:
void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override
{
attachAndroidJNI();
// only output stream should be the master stream receiving callbacks
jassert (stream->getDirection() == oboe::Direction::Output);
@@ -916,8 +912,6 @@ private:
void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
{
attachAndroidJNI();
// only output stream should be the master stream receiving callbacks
jassert (stream->getDirection() == oboe::Direction::Output);
@@ -987,23 +981,6 @@ private:
bool running = false;
//==============================================================================
static String audioManagerGetProperty (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
jProperty.get()));
if (text.get() != 0)
return juceString (text);
return {};
}
static bool androidHasSystemFeature (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
}
static double getNativeSampleRate()
{
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
@@ -1023,18 +1000,9 @@ private:
static int getDefaultFramesPerBurst()
{
// NB: this function only works for inbuilt speakers and headphones
auto* env = getEnv();
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getSystemService,
javaString ("audio").get()));
auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER");
auto framesPerBurstString = javaString (audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER"));
auto framesPerBurstString = LocalRef<jstring> ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
propertyJavaString.get()));
return framesPerBurstString != 0 ? env->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
return framesPerBurstString != 0 ? getEnv()->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice)
@@ -1051,7 +1019,7 @@ OboeAudioIODevice::OboeSessionBase* OboeAudioIODevice::OboeSessionBase::create (
{
std::unique_ptr<OboeSessionBase> session;
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
auto sdkVersion = getAndroidSDKVersion();
// SDK versions 21 and higher should natively support floating point...
if (sdkVersion >= 21)
@@ -1119,7 +1087,7 @@ public:
forInput ? oboe::Direction::Input : oboe::Direction::Output,
oboe::SharingMode::Shared,
forInput ? 1 : 2,
getSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
(int) OboeAudioIODevice::getNativeSampleRate(),
OboeAudioIODevice::getNativeBufferSize(),
nullptr);
@@ -1210,8 +1178,8 @@ public:
if (audioManagerClass == 0)
return;
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getSystemService,
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(),
AndroidContext.getSystemService,
javaString ("audio").get()));
static jmethodID getDevicesMethod = env->GetMethodID (audioManagerClass, "getDevices",
@@ -1253,16 +1221,10 @@ public:
bool supportsDevicesInfo() const
{
static auto result = getSDKVersion() >= 23;
static auto result = getAndroidSDKVersion() >= 23;
return result;
}
int getSDKVersion() const
{
static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
return sdkVersion;
}
void addDevice (const LocalRef<jobject>& device, JNIEnv* env)
{
auto deviceClass = LocalRef<jclass> ((jclass) env->FindClass ("android/media/AudioDeviceInfo"));


+ 10
- 28
modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -23,6 +23,10 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
DECLARE_JNI_CLASS (AndroidAudioManager, "android/media/AudioManager")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#ifndef SL_ANDROID_DATAFORMAT_PCM_EX
#define SL_ANDROID_DATAFORMAT_PCM_EX ((SLuint32) 0x00000004)
@@ -323,7 +327,7 @@ public:
if (runner == nullptr)
return false;
const bool supportsJavaProxy = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
const bool supportsJavaProxy = (getAndroidSDKVersion() >= 24);
if (supportsJavaProxy)
{
@@ -337,7 +341,7 @@ public:
&audioRoutingJni);
if (status == SL_RESULT_SUCCESS && audioRoutingJni != 0)
javaProxy = GlobalRef (audioRoutingJni);
javaProxy = GlobalRef (LocalRef<jobject>(getEnv()->NewLocalRef (audioRoutingJni)));
}
}
@@ -372,8 +376,6 @@ public:
void finished (SLAndroidSimpleBufferQueueItf)
{
attachAndroidJNI();
--numBlocksOut;
owner.doSomeWorkOnAudioThread();
}
@@ -489,8 +491,7 @@ public:
{
if (Base::config != nullptr)
{
const bool supportsUnprocessed = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 25);
const bool supportsUnprocessed = (getAndroidSDKVersion() >= 25);
const SLuint32 recordingPresetValue
= (shouldEnable ? SL_ANDROID_RECORDING_PRESET_GENERIC
: (supportsUnprocessed ? SL_ANDROID_RECORDING_PRESET_UNPROCESSED
@@ -660,7 +661,7 @@ public:
return;
}
const bool supportsUnderrunCount = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
getUnderrunCount = supportsUnderrunCount ? getEnv()->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0;
}
}
@@ -1048,9 +1049,7 @@ private:
// "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required
// for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one
// is sufficient for lower latency."
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
return (sdkVersion >= 18 ? 1 : 2);
return (getAndroidSDKVersion() >= 18 ? 1 : 2);
}
// we will not use the low-latency path so we can use the absolute minimum number of buffers
@@ -1079,23 +1078,6 @@ private:
}
//==============================================================================
static String audioManagerGetProperty (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
jProperty.get()));
if (text.get() != 0)
return juceString (text);
return {};
}
static bool androidHasSystemFeature (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
}
static double getNativeSampleRate()
{
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
@@ -1147,7 +1129,7 @@ OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (
int numBuffersToUse)
{
std::unique_ptr<OpenSLSession> retval;
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
auto sdkVersion = getAndroidSDKVersion();
// SDK versions 21 and higher should natively support floating point...
if (sdkVersion >= 21)


+ 24
- 9
modules/juce_audio_utils/native/juce_android_BluetoothMidiDevicePairingDialogue.cpp View File

@@ -27,7 +27,13 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$BluetoothManager;")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidJuceMidiSupport, "com/roli/juce/JuceMidiSupport", 23)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getMidiBluetoothAddresses, "getMidiBluetoothAddresses", "()[Ljava/lang/String;") \
METHOD (pairBluetoothMidiDevice, "pairBluetoothMidiDevice", "(Ljava/lang/String;)Z") \
METHOD (unpairBluetoothMidiDevice, "unpairBluetoothMidiDevice", "(Ljava/lang/String;)V") \
@@ -35,7 +41,7 @@ namespace juce
METHOD (getBluetoothDeviceStatus, "getBluetoothDeviceStatus", "(Ljava/lang/String;)I") \
METHOD (startStopScan, "startStopScan", "(Z)V")
DECLARE_JNI_CLASS (AndroidBluetoothManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidBluetoothManager, "com/roli/juce/JuceMidiSupport$BluetoothManager", 23)
#undef JNI_CLASS_MEMBERS
//==============================================================================
@@ -44,7 +50,7 @@ struct AndroidBluetoothMidiInterface
static void startStopScan (bool startScanning)
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
if (btManager.get() != nullptr)
env->CallVoidMethod (btManager.get(), AndroidBluetoothManager.startStopScan, (jboolean) (startScanning ? 1 : 0));
@@ -56,7 +62,7 @@ struct AndroidBluetoothMidiInterface
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
// if this is null then bluetooth is not enabled
if (btManager.get() == nullptr)
@@ -82,7 +88,7 @@ struct AndroidBluetoothMidiInterface
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
if (btManager.get() == nullptr)
return false;
@@ -96,7 +102,7 @@ struct AndroidBluetoothMidiInterface
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
if (btManager.get() != nullptr)
env->CallVoidMethod (btManager.get(), AndroidBluetoothManager.unpairBluetoothMidiDevice,
@@ -108,7 +114,7 @@ struct AndroidBluetoothMidiInterface
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
if (btManager.get() == nullptr)
return address;
@@ -136,7 +142,7 @@ struct AndroidBluetoothMidiInterface
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
if (btManager.get() == nullptr)
return unpaired;
@@ -485,6 +491,10 @@ bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback*
Rectangle<int>* btBounds)
{
std::unique_ptr<ModalComponentManager::Callback> exitCallback (exitCallbackPtr);
if (getAndroidSDKVersion() < 23)
return false;
auto boundsToUse = (btBounds != nullptr ? *btBounds : Rectangle<int> {});
if (! RuntimePermissions::isGranted (RuntimePermissions::bluetoothMidi))
@@ -502,7 +512,12 @@ bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback*
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
jobject btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
if (getAndroidSDKVersion() < 23)
return false;
auto* env = getEnv();
LocalRef<jobject> btManager (env->CallStaticObjectMethod (AndroidJuceMidiSupport, AndroidJuceMidiSupport.getAndroidBluetoothManager, getAppContext().get()));
return btManager != nullptr;
}


+ 2
- 0
modules/juce_core/juce_core.cpp View File

@@ -221,7 +221,9 @@
//==============================================================================
#elif JUCE_ANDROID
#include "native/juce_linux_CommonFile.cpp"
#include "native/juce_android_JNIHelpers.cpp"
#include "native/juce_android_Files.cpp"
#include "native/juce_android_Misc.cpp"
#include "native/juce_android_Network.cpp"


+ 0
- 169
modules/juce_core/native/java/AndroidCamera.java View File

@@ -1,169 +0,0 @@
$$CameraApi21
//==============================================================================
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
{
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
CameraDeviceStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onClosed (CameraDevice camera)
{
cameraDeviceStateClosed (host, camera);
}
@Override
public void onDisconnected (CameraDevice camera)
{
cameraDeviceStateDisconnected (host, camera);
}
@Override
public void onError (CameraDevice camera, int error)
{
cameraDeviceStateError (host, camera, error);
}
@Override
public void onOpened (CameraDevice camera)
{
cameraDeviceStateOpened (host, camera);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
{
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
CameraCaptureSessionStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onActive (CameraCaptureSession session)
{
cameraCaptureSessionActive (host, session);
}
@Override
public void onClosed (CameraCaptureSession session)
{
cameraCaptureSessionClosed (host, session);
}
@Override
public void onConfigureFailed (CameraCaptureSession session)
{
cameraCaptureSessionConfigureFailed (host, session);
}
@Override
public void onConfigured (CameraCaptureSession session)
{
cameraCaptureSessionConfigured (host, session);
}
@Override
public void onReady (CameraCaptureSession session)
{
cameraCaptureSessionReady (host, session);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
{
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
{
host = hostToUse;
preview = shouldBePreview;
}
@Override
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result)
{
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
}
@Override
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
{
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
}
@Override
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult)
{
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
}
@Override
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
{
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
}
@Override
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
{
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
}
@Override
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
long frameNumber)
{
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
}
private long host;
private boolean preview;
}
//==============================================================================
public class JuceOrientationEventListener extends OrientationEventListener
{
private native void deviceOrientationChanged (long host, int orientation);
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
{
super (context, rate);
host = hostToUse;
}
@Override
public void onOrientationChanged (int orientation)
{
deviceOrientationChanged (host, orientation);
}
private long host;
}
CameraApi21$$

+ 0
- 85
modules/juce_core/native/java/AndroidMidiFallback.java View File

@@ -1,85 +0,0 @@
//==============================================================================
public class BluetoothManager
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
String[] bluetoothAddresses = new String[0];
return bluetoothAddresses;
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
return address;
}
public int getBluetoothDeviceStatus (String address)
{
return 0;
}
public void startStopScan (boolean shouldStart)
{
}
public boolean pairBluetoothMidiDevice(String address)
{
return false;
}
public void unpairBluetoothMidiDevice (String address)
{
}
}
//==============================================================================
public class MidiDeviceManager
{
public MidiDeviceManager()
{
}
public String[] getJuceAndroidMidiInputDevices()
{
return new String[0];
}
public String[] getJuceAndroidMidiOutputDevices()
{
return new String[0];
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return null;
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return null;
}
public String getInputPortNameForJuceIndex (int index)
{
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
return "";
}
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
return null;
}
public BluetoothManager getAndroidBluetoothManager()
{
return null;
}

+ 0
- 12
modules/juce_core/native/java/AndroidRuntimePermissions.java View File

@@ -1,12 +0,0 @@
@Override
public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults)
{
boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
if (! permissionsGranted)
Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID));
Long ptrToCallback = permissionCallbackPtrMap.get (permissionID);
permissionCallbackPtrMap.remove (permissionID);
androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback);
}

+ 0
- 146
modules/juce_core/native/java/AndroidVideo.java View File

@@ -1,146 +0,0 @@
$$VideoApi21
//==============================================================================
public class MediaControllerCallback extends MediaController.Callback
{
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
private native void mediaControllerSessionDestroyed (long host);
MediaControllerCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
{
mediaControllerAudioInfoChanged (host, info);
}
@Override
public void onMetadataChanged (MediaMetadata metadata)
{
mediaControllerMetadataChanged (host, metadata);
}
@Override
public void onPlaybackStateChanged (PlaybackState state)
{
mediaControllerPlaybackStateChanged (host, state);
}
@Override
public void onQueueChanged (List<MediaSession.QueueItem> queue) {}
@Override
public void onSessionDestroyed()
{
mediaControllerSessionDestroyed (host);
}
private long host;
}
//==============================================================================
public class MediaSessionCallback extends MediaSession.Callback
{
private native void mediaSessionPause (long host);
private native void mediaSessionPlay (long host);
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
private native void mediaSessionSeekTo (long host, long pos);
private native void mediaSessionStop (long host);
MediaSessionCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onPause()
{
mediaSessionPause (host);
}
@Override
public void onPlay()
{
mediaSessionPlay (host);
}
@Override
public void onPlayFromMediaId (String mediaId, Bundle extras)
{
mediaSessionPlayFromMediaId (host, mediaId, extras);
}
@Override
public void onSeekTo (long pos)
{
mediaSessionSeekTo (host, pos);
}
@Override
public void onStop()
{
mediaSessionStop (host);
}
@Override
public void onFastForward() {}
@Override
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
{
return true;
}
@Override
public void onRewind() {}
@Override
public void onSkipToNext() {}
@Override
public void onSkipToPrevious() {}
@Override
public void onSkipToQueueItem (long id) {}
private long host;
}
//==============================================================================
public class SystemVolumeObserver extends ContentObserver
{
private native void mediaSessionSystemVolumeChanged (long host);
SystemVolumeObserver (Activity activityToUse, long hostToUse)
{
super (null);
activity = activityToUse;
host = hostToUse;
}
void setEnabled (boolean shouldBeEnabled)
{
if (shouldBeEnabled)
activity.getApplicationContext().getContentResolver().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
else
activity.getApplicationContext().getContentResolver().unregisterContentObserver (this);
}
@Override
public void onChange (boolean selfChange, Uri uri)
{
if (uri.toString().startsWith ("content://settings/system/volume_music"))
mediaSessionSystemVolumeChanged (host);
}
private Activity activity;
private long host;
}
VideoApi21$$

+ 0
- 69
modules/juce_core/native/java/AndroidWebView.java View File

@@ -1,69 +0,0 @@
$$WebViewNativeApi23 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error);WebViewNativeApi23$$
$$WebViewNativeApi21 private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse);WebViewNativeApi21$$
$$WebViewApi1_10
@Override
public void onPageStarted (WebView view, String url, Bitmap favicon)
{
if (host != 0)
webViewPageLoadStarted (host, view, url);
}
WebViewApi1_10$$
$$WebViewApi11_20
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, url);
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi11_20$$
$$WebViewApi21
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi21$$
$$WebViewApi23
@Override
public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
{
if (host == 0)
return;
webViewReceivedError (host, view, request, error);
}
@Override
public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
{
if (host == 0)
return;
webViewReceivedHttpError (host, view, request, errorResponse);
}
WebViewApi23$$

+ 0
- 971
modules/juce_core/native/java/IInAppBillingService.java View File

@@ -1,971 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.android.vending.billing;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
* RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
* RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
public interface IInAppBillingService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IInAppBillingService
{
private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IInAppBillingService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.vending.billing.IInAppBillingService interface,
* generating a proxy if needed.
*/
public static com.android.vending.billing.IInAppBillingService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.vending.billing.IInAppBillingService))) {
return ((com.android.vending.billing.IInAppBillingService)iin);
}
return new com.android.vending.billing.IInAppBillingService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_isBillingSupported:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.isBillingSupported(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getSkuDetails:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _result = this.getBuyIntent(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchases:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _result = this.getPurchases(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_consumePurchase:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.consumePurchase(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_stub:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.stub(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getBuyIntentToReplaceSkus:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.util.List<java.lang.String> _arg2;
_arg2 = data.createStringArrayList();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
java.lang.String _arg5;
_arg5 = data.readString();
android.os.Bundle _result = this.getBuyIntentToReplaceSkus(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntentExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _arg5;
if ((0!=data.readInt())) {
_arg5 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg5 = null;
}
android.os.Bundle _result = this.getBuyIntentExtraParams(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchaseHistory:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _arg4;
if ((0!=data.readInt())) {
_arg4 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg4 = null;
}
android.os.Bundle _result = this.getPurchaseHistory(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_isBillingSupportedExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
int _result = this.isBillingSupportedExtraParams(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.android.vending.billing.IInAppBillingService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_isBillingSupported, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
@Override public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((skusBundle!=null)) {
_data.writeInt(1);
skusBundle.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntent, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
mRemote.transact(Stub.TRANSACTION_getPurchases, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(purchaseToken);
mRemote.transact(Stub.TRANSACTION_consumePurchase, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_stub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeStringList(oldSkus);
_data.writeString(newSku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntentToReplaceSkus, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
@Override public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getBuyIntentExtraParams, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getPurchaseHistory, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_isBillingSupportedExtraParams, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_isBillingSupported = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getSkuDetails = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBuyIntent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPurchases = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_consumePurchase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_stub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_getBuyIntentToReplaceSkus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_getBuyIntentExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getPurchaseHistory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_isBillingSupportedExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
}
public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException;
public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException;
public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException;
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException;
public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException;
}

+ 0
- 1806
modules/juce_core/native/java/JuceAppActivity.java
File diff suppressed because it is too large
View File


+ 0
- 16
modules/juce_core/native/java/JuceFirebaseInstanceIdService.java View File

@@ -1,16 +0,0 @@
package com.juce;
import com.google.firebase.iid.*;
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService
{
private native void firebaseInstanceIdTokenRefreshed (String token);
@Override
public void onTokenRefresh()
{
String token = FirebaseInstanceId.getInstance().getToken();
firebaseInstanceIdTokenRefreshed (token);
}
}

+ 0
- 35
modules/juce_core/native/java/JuceFirebaseMessagingService.java View File

@@ -1,35 +0,0 @@
package com.juce;
import com.google.firebase.messaging.*;
public final class JuceFirebaseMessagingService extends FirebaseMessagingService
{
private native void firebaseRemoteMessageReceived (RemoteMessage message);
private native void firebaseRemoteMessagesDeleted();
private native void firebaseRemoteMessageSent (String messageId);
private native void firebaseRemoteMessageSendError (String messageId, String error);
@Override
public void onMessageReceived (RemoteMessage message)
{
firebaseRemoteMessageReceived (message);
}
@Override
public void onDeletedMessages()
{
firebaseRemoteMessagesDeleted();
}
@Override
public void onMessageSent (String messageId)
{
firebaseRemoteMessageSent (messageId);
}
@Override
public void onSendError (String messageId, Exception e)
{
firebaseRemoteMessageSendError (messageId, e.toString());
}
}

+ 19
- 0
modules/juce_core/native/java/README.txt View File

@@ -0,0 +1,19 @@
The java code in the module's native/java subfolders were used to generate dex byte-code in various places in the JUCE framework. These are the steps required to re-generate the dex byte-code from any java source code inside the native/java subfolders:

1. Create a new JUCE android project with the minimal sdk version which is required for the java source code you wish to compile.
2. Move the the .java files that you wish to create source code for, into the module's native/javacore/app folder. Remember that .java files need to be in nested sub-folders which resemble their package, i.e. a Java class com.roli.juce.HelloWorld.java should be in the module's native/javacore/app/com/roli/juce folder.
3. Build your project with AS and run. The app will now use the source code in the folder creates in step 2 - so you can debug your java code this way.
4. Once everything is working, rebuild your app in release mode.
5. Go to your app's Builds/Android folder. Inside there you will find app/build/intermediates/classes/release_/release. Inside of that folder, you will find all your java byte-code compiled classes. Remove any classes that you are not interested in (typically you'll find Java.class, JuceApp.class and JuceSharingContentProvider.class which you will probably want to remove).
6. Inside of app/build/intermediates/classes/release_/release execute the following dx command:

<path-to-your-android-sdk>/build-tools/<latest-build-tool-version>/dx --dex --verbose --min-sdk-version=<your-min-sdk-of-your-classes> --output /tmp/JavaDexByteCode.dex .


Replace <your-min-sdk-of-your-classes> with the minimal sdk version you used in step 1.
7. gzip the output:

gzip /tmp/JavaDexByteCode.dex

8. The output /tmp/JavaDexByteCode.dex.gz is now the byte code that can be included into JUCE. You can use the Projucer's BinaryData generator functionality to get this into a convenient char array like form.

+ 58
- 0
modules/juce_core/native/java/com/roli/juce/FragmentOverlay.java View File

@@ -0,0 +1,58 @@
package com.roli.juce;

import android.app.DialogFragment;
import android.content.Intent;
import android.os.Bundle;

public class FragmentOverlay extends DialogFragment
{
@Override
public void onCreate (Bundle state)
{
super.onCreate (state);
cppThis = getArguments ().getLong ("cppThis");

if (cppThis != 0)
onCreateNative (cppThis, state);
}

@Override
public void onStart ()
{
super.onStart ();

if (cppThis != 0)
onStartNative (cppThis);
}

public void onRequestPermissionsResult (int requestCode,
String[] permissions,
int[] grantResults)
{
if (cppThis != 0)
onRequestPermissionsResultNative (cppThis, requestCode,
permissions, grantResults);
}

@Override
public void onActivityResult (int requestCode, int resultCode, Intent data)
{
if (cppThis != 0)
onActivityResultNative (cppThis, requestCode, resultCode, data);
}

public void close ()
{
cppThis = 0;
dismiss ();
}

//==============================================================================
private long cppThis = 0;

private native void onActivityResultNative (long myself, int requestCode, int resultCode, Intent data);
private native void onCreateNative (long myself, Bundle state);
private native void onStartNative (long myself);
private native void onRequestPermissionsResultNative (long myself, int requestCode,
String[] permissions, int[] grantResults);
}

+ 407
- 0
modules/juce_core/native/java/com/roli/juce/JuceHTTPStream.java View File

@@ -0,0 +1,407 @@
package com.roli.juce;

import java.lang.Runnable;
import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.*;

public class JuceHTTPStream
{
public JuceHTTPStream(String address, boolean isPostToUse, byte[] postDataToUse,
String headersToUse, int timeOutMsToUse,
int[] statusCodeToUse, StringBuffer responseHeadersToUse,
int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
{
isPost = isPostToUse;
postData = postDataToUse;
headers = headersToUse;
timeOutMs = timeOutMsToUse;
statusCode = statusCodeToUse;
responseHeaders = responseHeadersToUse;
totalLength = -1;
numRedirectsToFollow = numRedirectsToFollowToUse;
httpRequestCmd = httpRequestCmdToUse;

connection = createConnection(address, isPost, postData, headers, timeOutMs, httpRequestCmd);
}

public static final JuceHTTPStream createHTTPStream(String address, boolean isPost, byte[] postData,
String headers, int timeOutMs, int[] statusCode,
StringBuffer responseHeaders, int numRedirectsToFollow,
String httpRequestCmd)
{
// timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
if (timeOutMs < 0)
timeOutMs = 0;
else if (timeOutMs == 0)
timeOutMs = 30000;

for (; ; )
{
try
{
JuceHTTPStream httpStream = new JuceHTTPStream(address, isPost, postData, headers,
timeOutMs, statusCode, responseHeaders,
numRedirectsToFollow, httpRequestCmd);

return httpStream;
} catch (Throwable e)
{
}

return null;
}
}

private final HttpURLConnection createConnection(String address, boolean isPost, byte[] postData,
String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
{
HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());

try
{
newConnection.setInstanceFollowRedirects(false);
newConnection.setConnectTimeout(timeOutMs);
newConnection.setReadTimeout(timeOutMs);

// headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
// So convert headers string to an array, with an element for each line
String headerLines[] = headers.split("\\n");

// Set request headers
for (int i = 0; i < headerLines.length; ++i)
{
int pos = headerLines[i].indexOf(":");

if (pos > 0 && pos < headerLines[i].length())
{
String field = headerLines[i].substring(0, pos);
String value = headerLines[i].substring(pos + 1);

if (value.length() > 0)
newConnection.setRequestProperty(field, value);
}
}

newConnection.setRequestMethod(httpRequestCmd);

if (isPost)
{
newConnection.setDoOutput(true);

if (postData != null)
{
OutputStream out = newConnection.getOutputStream();
out.write(postData);
out.flush();
}
}

return newConnection;
} catch (Throwable e)
{
newConnection.disconnect();
throw new IOException("Connection error");
}
}

private final InputStream getCancellableStream(final boolean isInput) throws ExecutionException
{
synchronized (createFutureLock)
{
if (hasBeenCancelled.get())
return null;

streamFuture = executor.submit(new Callable<BufferedInputStream>()
{
@Override
public BufferedInputStream call() throws IOException
{
return new BufferedInputStream(isInput ? connection.getInputStream()
: connection.getErrorStream());
}
});
}

try
{
return streamFuture.get();
} catch (InterruptedException e)
{
return null;
} catch (CancellationException e)
{
return null;
}
}

public final boolean connect()
{
boolean result = false;
int numFollowedRedirects = 0;

while (true)
{
result = doConnect();

if (!result)
return false;

if (++numFollowedRedirects > numRedirectsToFollow)
break;

int status = statusCode[0];

if (status == 301 || status == 302 || status == 303 || status == 307)
{
// Assumes only one occurrence of "Location"
int pos1 = responseHeaders.indexOf("Location:") + 10;
int pos2 = responseHeaders.indexOf("\n", pos1);

if (pos2 > pos1)
{
String currentLocation = connection.getURL().toString();
String newLocation = responseHeaders.substring(pos1, pos2);

try
{
// Handle newLocation whether it's absolute or relative
URL baseUrl = new URL(currentLocation);
URL newUrl = new URL(baseUrl, newLocation);
String transformedNewLocation = newUrl.toString();

if (transformedNewLocation != currentLocation)
{
// Clear responseHeaders before next iteration
responseHeaders.delete(0, responseHeaders.length());

synchronized (createStreamLock)
{
if (hasBeenCancelled.get())
return false;

connection.disconnect();

try
{
connection = createConnection(transformedNewLocation, isPost,
postData, headers, timeOutMs,
httpRequestCmd);
} catch (Throwable e)
{
return false;
}
}
} else
{
break;
}
} catch (Throwable e)
{
return false;
}
} else
{
break;
}
} else
{
break;
}
}

return result;
}

private final boolean doConnect()
{
synchronized (createStreamLock)
{
if (hasBeenCancelled.get())
return false;

try
{
try
{
inputStream = getCancellableStream(true);
} catch (ExecutionException e)
{
if (connection.getResponseCode() < 400)
{
statusCode[0] = connection.getResponseCode();
connection.disconnect();
return false;
}
} finally
{
statusCode[0] = connection.getResponseCode();
}

try
{
if (statusCode[0] >= 400)
inputStream = getCancellableStream(false);
else
inputStream = getCancellableStream(true);
} catch (ExecutionException e)
{
}

for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
{
if (entry.getKey() != null && entry.getValue() != null)
{
responseHeaders.append(entry.getKey() + ": "
+ android.text.TextUtils.join(",", entry.getValue()) + "\n");

if (entry.getKey().compareTo("Content-Length") == 0)
totalLength = Integer.decode(entry.getValue().get(0));
}
}

return true;
} catch (IOException e)
{
return false;
}
}
}

static class DisconnectionRunnable implements Runnable
{
public DisconnectionRunnable(HttpURLConnection theConnection,
InputStream theInputStream,
ReentrantLock theCreateStreamLock,
Object theCreateFutureLock,
Future<BufferedInputStream> theStreamFuture)
{
connectionToDisconnect = theConnection;
inputStream = theInputStream;
createStreamLock = theCreateStreamLock;
createFutureLock = theCreateFutureLock;
streamFuture = theStreamFuture;
}

public void run()
{
try
{
if (!createStreamLock.tryLock())
{
synchronized (createFutureLock)
{
if (streamFuture != null)
streamFuture.cancel(true);
}

createStreamLock.lock();
}

if (connectionToDisconnect != null)
connectionToDisconnect.disconnect();

if (inputStream != null)
inputStream.close();
} catch (IOException e)
{
} finally
{
createStreamLock.unlock();
}
}

private HttpURLConnection connectionToDisconnect;
private InputStream inputStream;
private ReentrantLock createStreamLock;
private Object createFutureLock;
Future<BufferedInputStream> streamFuture;
}

public final void release()
{
DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable(connection,
inputStream,
createStreamLock,
createFutureLock,
streamFuture);

synchronized (createStreamLock)
{
hasBeenCancelled.set(true);

connection = null;
}

Thread disconnectionThread = new Thread(disconnectionRunnable);
disconnectionThread.start();
}

public final int read(byte[] buffer, int numBytes)
{
int num = 0;

try
{
synchronized (createStreamLock)
{
if (inputStream != null)
num = inputStream.read(buffer, 0, numBytes);
}
} catch (IOException e)
{
}

if (num > 0)
position += num;

return num;
}

public final long getPosition()
{
return position;
}

public final long getTotalLength()
{
return totalLength;
}

public final boolean isExhausted()
{
return false;
}

public final boolean setPosition(long newPos)
{
return false;
}

private boolean isPost;
private byte[] postData;
private String headers;
private int timeOutMs;
String httpRequestCmd;
private HttpURLConnection connection;
private int[] statusCode;
private StringBuffer responseHeaders;
private int totalLength;
private int numRedirectsToFollow;
private InputStream inputStream;
private long position;
private final ReentrantLock createStreamLock = new ReentrantLock();
private final Object createFutureLock = new Object();
private AtomicBoolean hasBeenCancelled = new AtomicBoolean();

private final ExecutorService executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
Future<BufferedInputStream> streamFuture;
}

+ 15
- 0
modules/juce_core/native/javacore/app/com/roli/juce/JuceApp.java View File

@@ -0,0 +1,15 @@
package com.roli.juce;
import com.roli.juce.Java;
import android.app.Application;
public class JuceApp extends Application
{
@Override
public void onCreate ()
{
super.onCreate ();
Java.initialiseJUCE (this);
}
}

+ 13
- 0
modules/juce_core/native/javacore/init/com/roli/juce/Java.java View File

@@ -0,0 +1,13 @@
package com.roli.juce;
import android.content.Context;
public class Java
{
static
{
System.loadLibrary ("juce_jni");
}
public native static void initialiseJUCE (Context appContext);
}

+ 118
- 65
modules/juce_core/native/juce_android_Files.cpp View File

@@ -23,7 +23,7 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;Landroid/media/MediaScannerConnection$MediaScannerConnectionClient;)V") \
METHOD (connect, "connect", "()V") \
METHOD (disconnect, "disconnect", "()V") \
@@ -32,7 +32,7 @@ namespace juce
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \
METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;")
@@ -40,7 +40,7 @@ DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (moveToFirst, "moveToFirst", "()Z") \
METHOD (getColumnIndex, "getColumnIndex", "(Ljava/lang/String;)I") \
METHOD (getString, "getString", "(I)Ljava/lang/String;") \
@@ -49,14 +49,15 @@ DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver")
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getExternalStorageDirectory, "getExternalStorageDirectory", "()Ljava/io/File;") \
STATICMETHOD (getExternalStoragePublicDirectory, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;") \
STATICMETHOD (getDataDirectory, "getDataDirectory", "()Ljava/io/File;")
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (flush, "flush", "()V") \
METHOD (write, "write", "([BII)V")
@@ -64,6 +65,55 @@ DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment")
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (publicSourceDir, "publicSourceDir", "Ljava/lang/String;") \
FIELD (dataDir, "dataDir", "Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidApplicationInfo, "android/content/pm/ApplicationInfo")
#undef JNI_CLASS_MEMBERS
//==============================================================================
static File juceFile (LocalRef<jobject> obj)
{
auto* env = getEnv();
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
JavaFile.getAbsolutePath))));
return {};
}
static File getWellKnownFolder (const char* folderId)
{
auto* env = getEnv();
auto fieldId = env->GetStaticFieldID (AndroidEnvironment, folderId, "Ljava/lang/String;");
if (fieldId == 0)
{
// unknown field in environment
jassertfalse;
return {};
}
LocalRef<jobject> fieldValue (env->GetStaticObjectField (AndroidEnvironment, fieldId));
if (fieldValue == nullptr)
return {};
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
AndroidEnvironment.getExternalStoragePublicDirectory,
fieldValue.get()));
return (downloadFolder ? juceFile (downloadFolder) : File());
}
static LocalRef<jobject> urlToUri (const URL& url)
{
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
javaString (url.toString (true)).get()));
}
//==============================================================================
struct AndroidContentUriResolver
{
@@ -74,7 +124,7 @@ public:
jassert (url.getScheme() == "content");
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver)
return LocalRef<jobject> ((env->CallObjectMethod (contentResolver.get(),
@@ -116,7 +166,7 @@ public:
else if (type.equalsIgnoreCase ("downloads"))
{
auto subDownloadPath = url.getSubPath().fromFirstOccurrenceOf ("tree/downloads", false, false);
return File (getWellKnownFolder ("Download").getFullPathName() + "/" + subDownloadPath);
return File (getWellKnownFolder ("DIRECTORY_DOWNLOADS").getFullPathName() + "/" + subDownloadPath);
}
else
{
@@ -142,7 +192,7 @@ public:
{
auto uri = urlToUri (url);
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver == 0)
return {};
@@ -166,7 +216,7 @@ private:
{
auto uri = urlToUri (url);
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver)
{
@@ -217,17 +267,6 @@ private:
return {};
}
//==============================================================================
static File getWellKnownFolder (const String& folderId)
{
auto* env = getEnv();
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
AndroidEnvironment.getExternalStoragePublicDirectory,
javaString (folderId).get()));
return (downloadFolder ? juceFile (downloadFolder) : File());
}
//==============================================================================
static File getStorageDevicePath (const String& storageId)
{
@@ -254,15 +293,15 @@ private:
{
Array<File> results;
if (getSDKVersion() >= 19)
if (getAndroidSDKVersion() >= 19)
{
auto* env = getEnv();
static jmethodID m = (env->GetMethodID (JuceAppActivity, "getExternalFilesDirs",
static jmethodID m = (env->GetMethodID (AndroidContext, "getExternalFilesDirs",
"(Ljava/lang/String;)[Ljava/io/File;"));
if (m == 0)
return {};
auto paths = convertFileArray (LocalRef<jobject> (android.activity.callObjectMethod (m, nullptr)));
auto paths = convertFileArray (LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), m, nullptr)));
for (auto path : paths)
results.add (getMountPointForFile (path));
@@ -348,33 +387,6 @@ private:
return files;
}
static File juceFile (LocalRef<jobject> obj)
{
auto* env = getEnv();
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
JavaFile.getAbsolutePath))));
return {};
}
//==============================================================================
static int getSDKVersion()
{
static int sdkVersion
= getEnv()->CallStaticIntMethod (JuceAppActivity,
JuceAppActivity.getAndroidSDKVersion);
return sdkVersion;
}
static LocalRef<jobject> urlToUri (const URL& url)
{
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
javaString (url.toString (true)).get()));
}
//==============================================================================
static String getStringUsingDataColumn (const String& columnNameToUse, JNIEnv* env,
const LocalRef<jobject>& uri,
@@ -525,9 +537,24 @@ String File::getVersion() const
return {};
}
static File getSpecialFile (jmethodID type)
static File getDocumentsDirectory()
{
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type))));
auto* env = getEnv();
if (getAndroidSDKVersion() >= 19)
return getWellKnownFolder ("DIRECTORY_DOCUMENTS");
return juceFile (LocalRef<jobject> (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getDataDirectory)));
}
static File getAppDataDir (bool dataDir)
{
auto* env = getEnv();
LocalRef<jobject> applicationInfo (env->CallObjectMethod (getAppContext().get(), AndroidContext.getApplicationInfo));
LocalRef<jobject> jString (env->GetObjectField (applicationInfo.get(), dataDir ? AndroidApplicationInfo.dataDir : AndroidApplicationInfo.publicSourceDir));
return {juceString ((jstring) jString.get())};
}
File File::getSpecialLocation (const SpecialLocationType type)
@@ -538,20 +565,41 @@ File File::getSpecialLocation (const SpecialLocationType type)
case userApplicationDataDirectory:
case userDesktopDirectory:
case commonApplicationDataDirectory:
return File (android.appDataDir);
{
static File appDataDir = getAppDataDir (true);
return appDataDir;
}
case userDocumentsDirectory:
case commonDocumentsDirectory: return getSpecialFile (JuceAppActivity.getDocumentsFolder);
case userPicturesDirectory: return getSpecialFile (JuceAppActivity.getPicturesFolder);
case userMusicDirectory: return getSpecialFile (JuceAppActivity.getMusicFolder);
case userMoviesDirectory: return getSpecialFile (JuceAppActivity.getMoviesFolder);
case commonDocumentsDirectory:
{
static auto docsDir = getDocumentsDirectory();
return docsDir;
}
case userPicturesDirectory:
{
static auto picturesDir = getWellKnownFolder ("DIRECTORY_PICTURES");
return picturesDir;
}
case userMusicDirectory:
{
static auto musicDir = getWellKnownFolder ("DIRECTORY_MUSIC");
return musicDir;
}
case userMoviesDirectory:
{
static auto moviesDir = getWellKnownFolder ("DIRECTORY_MOVIES");
return moviesDir;
}
case globalApplicationsDirectory:
return File ("/system/app");
case tempDirectory:
{
File tmp = File (android.appDataDir).getChildFile (".temp");
File tmp = getSpecialLocation (commonApplicationDataDirectory).getChildFile (".temp");
tmp.createDirectory();
return File (tmp.getFullPathName());
}
@@ -560,7 +608,7 @@ File File::getSpecialLocation (const SpecialLocationType type)
case currentExecutableFile:
case currentApplicationFile:
case hostApplicationPath:
return juce_getExecutableFile();
return getAppDataDir (false);
default:
jassertfalse; // unknown type?
@@ -581,8 +629,13 @@ bool File::moveToTrash() const
JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&)
{
const LocalRef<jstring> t (javaString (fileName));
android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get());
URL targetURL (File {fileName});
auto* env = getEnv();
const LocalRef<jstring> action (javaString ("android.intent.action.VIEW"));
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithUri, action.get(), urlToUri (targetURL).get()));
env->CallVoidMethod (getCurrentActivity(), AndroidContext.startActivity, intent.get());
return true;
}
@@ -595,10 +648,10 @@ class SingleMediaScanner : public MediaScannerConnectionClient
{
public:
SingleMediaScanner (const String& filename)
: msc (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
android.activity.get(),
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get())),
: msc (LocalRef<jobject> (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
getAppContext().get(),
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get()))),
file (filename)
{
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect);


+ 687
- 0
modules/juce_core/native/juce_android_JNIHelpers.cpp View File

@@ -0,0 +1,687 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
static const uint8 invocationHandleByteCode[] = {
31,139,8,8,170,94,229,91,0,3,105,110,118,111,99,97,116,105,111,110,95,104,97,110,100,108,101,114,46,100,101,120,0,109,
148,75,107,19,81,20,199,207,189,119,38,169,181,109,98,218,138,72,23,81,20,68,193,169,218,130,144,42,130,162,53,140,15,
104,200,66,187,232,52,185,181,147,78,103,98,58,141,33,184,168,197,47,224,66,168,187,186,241,83,168,184,16,247,162,31,193,
69,151,174,220,40,234,255,62,154,4,237,192,239,62,206,57,115,30,115,239,153,186,236,12,79,95,154,165,159,187,91,95,227,
217,197,247,87,42,31,231,191,156,157,58,241,253,199,220,201,79,75,175,118,58,14,81,147,136,58,213,153,2,217,231,131,32,
154,32,35,63,164,246,0,102,244,13,48,48,129,33,139,121,138,153,125,5,131,131,119,82,204,203,156,168,1,214,65,10,186,224,
53,120,7,62,131,61,144,131,237,57,112,30,92,4,183,192,93,176,4,154,160,11,182,193,11,97,252,171,216,46,200,144,137,59,
100,243,26,6,35,0,46,9,166,116,131,155,89,113,159,27,125,214,214,116,216,174,23,185,241,89,208,181,8,173,99,240,48,106,
247,99,182,198,156,149,231,245,204,232,136,246,203,173,189,65,189,61,7,209,31,60,167,48,239,26,119,90,183,35,84,190,66,
175,201,230,218,204,43,201,81,172,30,76,131,25,66,180,190,133,169,81,107,139,164,243,112,123,25,154,253,194,53,232,17,
231,2,74,190,140,106,212,190,57,205,201,97,99,168,207,209,223,71,61,147,202,182,57,104,59,74,11,45,212,255,56,251,60,251,
50,251,166,157,81,81,71,80,83,129,206,252,238,133,239,101,162,242,72,237,217,186,246,251,171,106,51,248,242,162,183,218,
183,207,204,133,113,152,94,37,86,38,215,47,251,190,79,142,175,198,211,126,45,89,247,90,73,20,122,141,205,154,244,202,24,
110,199,237,164,22,164,97,18,207,7,113,61,146,173,18,29,247,235,65,212,14,215,188,32,142,147,84,235,188,202,106,43,121,
178,81,162,130,223,8,218,129,23,5,241,35,239,222,114,67,214,210,18,77,14,200,180,93,176,28,201,18,162,245,197,45,185,18,
193,214,59,48,218,255,102,119,100,186,154,212,75,196,170,196,171,101,26,127,120,64,84,183,22,201,160,69,249,122,184,209,
12,210,218,234,205,48,14,162,176,43,105,108,95,162,130,173,73,26,90,217,215,100,66,35,25,141,145,66,91,94,79,226,84,118,
82,114,219,65,180,41,137,115,54,62,197,142,57,196,132,186,86,207,182,156,95,156,111,115,98,10,182,43,4,123,43,24,219,19,
185,127,206,70,247,159,237,77,62,208,159,98,160,71,157,129,62,117,169,223,171,25,234,247,171,200,155,181,62,231,162,121,
231,169,178,41,26,185,186,207,44,111,228,234,142,243,162,137,171,250,219,41,246,239,56,217,181,190,251,214,167,250,127,
252,5,76,239,47,69,120,4,0,0};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \
METHOD (clear, "clear", "()V") \
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/roli/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode))
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (findClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") \
STATICMETHOD (getSystemClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;")
DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V")
DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26)
#undef JNI_CLASS_MEMBERS
//==============================================================================
struct SystemJavaClassComparator
{
static int compareElements (JNIClassBase* first, JNIClassBase* second)
{
auto isSysClassA = isSystemClass (first);
auto isSysClassB = isSystemClass (second);
if ((! isSysClassA) && (! isSysClassB))
{
return DefaultElementComparator<bool>::compareElements (first != nullptr ? first->byteCode != nullptr : false,
second != nullptr ? second->byteCode != nullptr : false);
}
return DefaultElementComparator<bool>::compareElements (isSystemClass (first),
isSystemClass (second));
}
static bool isSystemClass (JNIClassBase* cls)
{
if (cls == nullptr)
return false;
String path (cls->getClassPath());
return path.startsWith ("java/")
|| path.startsWith ("android/")
|| path.startsWith ("dalvik/");
}
};
//==============================================================================
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n)
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (0)
{
SystemJavaClassComparator comparator;
getClasses().addSorted (comparator, this);
}
JNIClassBase::~JNIClassBase()
{
getClasses().removeFirstMatchingValue (this);
}
Array<JNIClassBase*>& JNIClassBase::getClasses()
{
static Array<JNIClassBase*> classes;
return classes;
}
// Get code cache directory without yet having a context object
static File getCodeCacheDirectory()
{
int pid = getpid();
File cmdline("/proc/" + String(pid) + "/cmdline");
auto bundleId = cmdline.loadFileAsString().trimStart().trimEnd();
if (bundleId.isEmpty())
return {};
return File("/data/data/" + bundleId + "/code_cache");
}
void JNIClassBase::initialise (JNIEnv* env)
{
auto sdkVersion = getAndroidSDKVersion();
if (sdkVersion >= minSDK)
{
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
static Array<GlobalRef> byteCodeLoaders;
if (! SystemJavaClassComparator::isSystemClass(this))
{
LocalRef<jobject> defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader));
tryLoadingClassWithClassLoader (env, defaultClassLoader.get());
if (classRef == 0)
{
for (auto& byteCodeLoader : byteCodeLoaders)
{
tryLoadingClassWithClassLoader (env, byteCodeLoader.get());
if (classRef != 0)
break;
}
// fallback by trying to load the class from bytecode
if (byteCode != nullptr)
{
LocalRef<jobject> byteCodeClassLoader;
MemoryOutputStream uncompressedByteCode;
{
MemoryInputStream rawGZipData (byteCode, byteCodeSize, false);
GZIPDecompressorInputStream gzipStream (&rawGZipData, false, GZIPDecompressorInputStream::gzipFormat);
uncompressedByteCode.writeFromInputStream (gzipStream, -1);
}
if (sdkVersion >= 26)
{
LocalRef<jbyteArray> byteArray (env->NewByteArray ((jsize) uncompressedByteCode.getDataSize()));
jboolean isCopy;
auto* dst = env->GetByteArrayElements (byteArray.get(), &isCopy);
memcpy (dst, uncompressedByteCode.getData(), uncompressedByteCode.getDataSize());
env->ReleaseByteArrayElements (byteArray.get(), dst, 0);
LocalRef<jobject> byteBuffer (env->CallStaticObjectMethod (JavaByteBuffer, JavaByteBuffer.wrap, byteArray.get()));
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidInMemoryDexClassLoader,
AndroidInMemoryDexClassLoader.constructor,
byteBuffer.get(), defaultClassLoader.get()));
}
else if (uncompressedByteCode.getDataSize() >= 32)
{
auto codeCacheDir = getCodeCacheDirectory();
// The dex file has an embedded 20-byte long SHA-1 signature at offset 12
auto fileName = String::toHexString ((char*)uncompressedByteCode.getData() + 12, 20, 0) + ".dex";
auto dexFile = codeCacheDir.getChildFile (fileName);
auto optimizedDirectory = codeCacheDir.getChildFile ("optimized_cache");
optimizedDirectory.createDirectory();
if (dexFile.replaceWithData (uncompressedByteCode.getData(), uncompressedByteCode.getDataSize()))
{
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidDexClassLoader,
AndroidDexClassLoader.constructor,
javaString (dexFile.getFullPathName()).get(),
javaString (optimizedDirectory.getFullPathName()).get(),
nullptr,
defaultClassLoader.get()));
}
else
{
// can't write to cache folder
jassertfalse;
}
}
if (byteCodeClassLoader != nullptr)
{
tryLoadingClassWithClassLoader (env, byteCodeClassLoader.get());
byteCodeLoaders.add (GlobalRef(byteCodeClassLoader));
}
}
}
}
if (classRef == 0)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
jassert (classRef != 0);
initialiseFields (env);
}
}
void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader)
{
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
// Android SDK <= 19 has a bug where the class loader might throw an exception but still return
// a non-nullptr. So don't assign the result of this call to a jobject just yet...
auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.findClass, classNameAndPackage.get());
if (jthrowable exception = env->ExceptionOccurred ())
{
env->ExceptionClear();
classObj = 0;
}
// later versions of Android don't throw at all, so re-check the object
if (classObj != nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj));
}
void JNIClassBase::release (JNIEnv* env)
{
if (classRef != 0)
env->DeleteGlobalRef (classRef);
}
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->initialise (env);
}
void JNIClassBase::releaseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->release (env);
}
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
void JNIClassBase::resolveCallbacks (JNIEnv* env, const Array<JNINativeMethod>& nativeCallbacks)
{
if (nativeCallbacks.size() > 0)
env->RegisterNatives (classRef, nativeCallbacks.begin(), (jint) nativeCallbacks.size());
}
//==============================================================================
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass)
{
auto* env = getEnv();
implementer->javaSubClass = GlobalRef (subclass);
// you need to override at least one interface
jassert (interfaceNames.size() > 0);
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
LocalRef<jobject> classLoader;
for (auto i = 0; i < interfaceNames.size(); ++i)
{
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
if (aClass != nullptr)
{
if (i == 0)
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
}
else
{
// interface class not found
jassertfalse;
}
}
auto invocationHandler = LocalRef<jobject> (env->NewObject (JuceInvocationHandler, JuceInvocationHandler.constructor,
reinterpret_cast<jlong> (implementer)));
// CreateJavaInterface() is expected to be called just once for a given implementer
jassert (implementer->invocationHandler == nullptr);
implementer->invocationHandler = GlobalRef (invocationHandler);
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
classLoader.get(), classArray.get(),
invocationHandler.get()));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames)
{
return CreateJavaInterface (implementer, interfaceNames,
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
JavaObject.constructor)));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName)
{
return CreateJavaInterface (implementer, StringArray (interfaceName));
}
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
{
clear();
}
void AndroidInterfaceImplementer::clear()
{
if (invocationHandler != nullptr)
getEnv()->CallVoidMethod (invocationHandler,
JuceInvocationHandler.clear);
}
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
{
auto* env = getEnv();
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
}
jobject juce_invokeImplementer (JNIEnv*, jobject /*object*/, jlong host, jobject proxy,
jobject method, jobjectArray args)
{
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
return myself->invoke (proxy, method, args);
return nullptr;
}
void juce_dispatchDelete (JNIEnv*, jobject /*object*/, jlong host)
{
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
delete myself;
}
//==============================================================================
jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobjectArray args)
{
auto* env = getEnv();
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr;
auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr;
if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; }
else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; }
else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; }
else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; }
else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; }
else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; }
else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; }
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
//==============================================================================
int getAndroidSDKVersion()
{
// this is used so often that we need to cache this
static int sdkVersion = []
{
// don't use any jni helpers as they might not have been initialised yet
// when this method is used
auto* env = getEnv();
auto buildVersion = env->FindClass ("android/os/Build$VERSION");
jassert (buildVersion != 0);
auto sdkVersionField = env->GetStaticFieldID (buildVersion, "SDK_INT", "I");
jassert (sdkVersionField != 0);
return env->GetStaticIntField (buildVersion, sdkVersionField);
}();
return sdkVersion;
}
bool isPermissionDeclaredInManifest (const String& requestedPermission)
{
auto* env = getEnv();
LocalRef<jobject> pkgManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
LocalRef<jobject> pkgName (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName));
LocalRef<jobject> pkgInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.getPackageInfo,
pkgName.get(), 0x00001000 /* PERMISSIONS */));
LocalRef<jobjectArray> permissions ((jobjectArray) env->GetObjectField (pkgInfo.get(), AndroidPackageInfo.requestedPermissions));
int n = env->GetArrayLength (permissions);
for (int i = 0; i < n; ++i)
{
LocalRef<jstring> jstr ((jstring) env->GetObjectArrayElement (permissions, i));
String permissionId (juceString (jstr));
if (permissionId == requestedPermission)
return true;
}
return false;
}
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/FragmentOverlay.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaFragmentOverlay[] =
{31,139,8,8,197,105,229,91,0,3,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149,93,136,
28,69,16,199,171,231,243,62,39,235,93,60,206,243,244,214,136,73,4,201,156,104,32,186,107,184,36,34,236,58,248,145,11,251,
112,241,101,216,29,215,137,123,51,155,153,217,35,1,65,61,2,201,131,8,10,126,97,2,17,84,16,204,91,30,228,240,73,130,95,40,
104,124,9,137,47,9,248,230,23,8,65,52,130,255,234,238,201,133,35,226,178,191,169,234,234,238,170,234,154,158,238,78,116,
100,100,254,129,157,116,170,242,197,47,171,219,126,253,250,218,79,107,167,30,234,189,251,229,123,127,239,158,219,187,86,
124,127,193,33,234,19,209,145,214,131,19,164,127,187,96,187,151,148,125,4,108,22,74,214,33,241,167,179,120,84,32,63,213,
237,186,65,244,130,69,244,12,228,121,147,232,34,248,29,252,1,174,130,191,192,63,224,118,140,217,9,154,224,121,240,34,88,
5,39,192,171,224,117,240,14,56,13,62,0,31,129,51,224,51,240,21,56,15,46,128,203,224,55,240,39,112,108,162,105,48,15,30,6,
77,240,44,56,1,94,3,167,193,25,176,6,62,7,223,2,164,73,72,135,176,76,114,193,16,24,214,107,29,5,147,188,102,0,247,114,
125,199,48,216,214,109,210,99,92,173,143,105,253,21,140,25,215,250,219,208,61,173,191,15,125,147,214,63,54,85,221,88,255,
4,250,45,90,63,7,125,66,235,223,200,88,130,166,136,243,52,100,12,3,217,221,169,219,91,116,30,51,196,227,84,63,203,91,181,
156,38,53,255,54,41,77,154,149,210,161,59,164,84,126,108,172,120,78,74,139,170,82,186,116,151,158,191,69,74,155,238,38,
181,102,65,164,163,40,157,127,67,142,146,38,44,108,251,193,86,53,236,87,120,44,103,190,84,229,10,148,253,151,116,127,217,
147,84,28,140,243,80,71,75,190,131,43,182,90,255,34,54,220,20,130,221,15,55,187,208,187,152,161,38,135,197,49,241,134,
251,225,138,51,12,95,30,241,76,94,255,207,152,195,107,74,171,130,14,192,163,11,235,24,205,136,41,74,170,38,170,60,74,75,
11,240,184,112,163,71,87,182,251,11,136,251,180,39,223,163,138,127,245,127,226,187,50,254,184,140,207,181,229,189,195,19,
249,253,165,21,206,231,166,113,230,55,145,37,60,93,55,71,239,57,210,82,233,195,178,46,66,83,238,55,238,45,117,30,97,72,
221,210,99,156,122,156,196,197,110,218,252,88,22,118,151,163,164,120,114,37,202,122,225,209,29,135,194,149,144,68,131,68,
147,140,102,64,34,160,217,32,76,58,89,26,119,252,176,223,247,31,141,195,94,218,45,103,213,104,250,122,111,59,77,10,152,
252,134,20,53,154,188,222,147,230,254,222,65,210,233,69,53,154,11,218,233,178,159,165,189,216,63,52,104,71,254,134,240,
53,154,8,56,3,191,23,38,93,127,177,200,226,164,91,35,209,34,171,213,104,4,252,12,2,50,90,77,178,91,77,54,176,128,197,108,
53,217,12,14,54,104,242,224,77,92,216,237,94,154,71,228,182,251,253,3,207,197,57,89,157,176,8,201,237,196,249,114,156,
231,52,214,141,138,61,89,119,192,169,228,228,162,21,164,73,23,230,44,76,138,253,81,62,232,193,92,73,147,61,237,34,94,137,
139,163,202,68,83,27,45,79,132,104,69,52,148,38,251,178,40,44,34,242,74,77,247,204,164,201,254,232,240,32,202,139,167,
162,140,67,199,105,146,107,111,213,255,238,211,179,221,52,89,44,194,172,160,113,173,104,251,104,127,125,2,141,102,202,
201,190,180,19,209,72,38,231,75,221,206,11,78,201,42,184,0,46,185,158,113,95,141,118,64,62,94,167,237,230,214,109,211,
174,119,252,77,26,19,219,93,175,126,238,248,18,85,205,173,247,204,194,246,22,190,57,215,123,4,22,18,54,62,111,235,229,
151,172,31,45,123,21,39,201,13,216,226,154,101,138,147,182,33,190,3,39,29,72,103,124,195,55,207,178,188,19,120,63,150,
247,130,73,235,119,67,185,103,249,126,224,179,163,188,35,28,90,191,39,68,85,181,249,174,16,21,117,46,240,249,106,84,149,
127,190,63,76,61,134,207,21,62,160,68,121,230,84,148,206,247,211,191,48,134,254,198,216,6,0,0};
//==============================================================================
#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")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/roli/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay))
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (show, "show", "(Landroid/app/FragmentManager;Ljava/lang/String;)V")
DECLARE_JNI_CLASS (AndroidDialogFragment, "android/app/DialogFragment")
#undef JNI_CLASS_MEMBERS
//==============================================================================
FragmentOverlay::FragmentOverlay()
: native (LocalRef<jobject> (getEnv()->NewObject (JuceFragmentOverlay, JuceFragmentOverlay.construct)))
{}
FragmentOverlay::~FragmentOverlay()
{
auto* env = getEnv();
env->CallVoidMethod (native.get(), JuceFragmentOverlay.close);
}
void FragmentOverlay::open()
{
auto* env = getEnv();
LocalRef<jobject> bundle (env->NewObject (AndroidBundle, AndroidBundle.constructor));
env->CallVoidMethod (bundle.get(), AndroidBundle.putLong, javaString ("cppThis").get(), (jlong) this);
env->CallVoidMethod (native.get(), AndroidFragment.setArguments, bundle.get());
LocalRef<jobject> fm (env->CallObjectMethod (getCurrentActivity().get(), AndroidActivity.getFragmentManager));
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)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
}
void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle)));
}
void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onStart();
}
void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode,
jobjectArray jPermissions, jintArray jGrantResults)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
{
Array<int> grantResults;
int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
if (n > 0)
{
auto* data = env->GetIntArrayElements (jGrantResults, 0);
for (int i = 0; i < n; ++i)
grantResults.add (data[i]);
env->ReleaseIntArrayElements (jGrantResults, data, 0);
}
myself->onRequestPermissionsResult (requestCode,
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
grantResults);
}
}
jobject FragmentOverlay::getNativeHandle()
{
return native.get();
}
//==============================================================================
class ActivityLauncher : public FragmentOverlay
{
public:
ActivityLauncher (const LocalRef<jobject>& intentToUse,
int requestCodeToUse,
std::function<void (int, int, LocalRef<jobject>)> && callbackToUse)
: intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse))
{}
void onStart() override
{
getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult,
intent.get(), requestCode);
}
void onActivityResult (int activityRequestCode, int resultCode, LocalRef<jobject> data) override
{
if (callback)
callback (activityRequestCode, resultCode, std::move (data));
getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close);
delete this;
}
private:
GlobalRef intent;
int requestCode;
std::function<void (int, int, LocalRef<jobject>)> callback;
};
void startAndroidActivityForResult (const LocalRef<jobject>& intent, int requestCode,
std::function<void (int, int, LocalRef<jobject>)> && callback)
{
auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback));
activityLauncher->open();
}
//==============================================================================
bool androidHasSystemFeature (const String& property)
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> packageManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
if (packageManager != nullptr)
return env->CallBooleanMethod (packageManager.get(),
AndroidPackageManager.hasSystemFeature,
javaString (property).get()) != 0;
}
// unable to get app's context
jassertfalse;
return false;
}
String audioManagerGetProperty (const String& property)
{
if (getAndroidSDKVersion() >= 17)
{
auto* env = getEnv();
LocalRef<jobject> audioManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService,
javaString ("audio").get()));
if (audioManager != nullptr)
{
LocalRef<jstring> jProperty (javaString (property));
auto methodID = env->GetMethodID (AndroidAudioManager, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
if (methodID != nullptr)
return juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (audioManager.get(),
methodID,
javaString (property).get())));
}
}
return {};
}
}

+ 354
- 215
modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -23,42 +23,81 @@
namespace juce
{
#if ! (defined (JUCE_ANDROID_ACTIVITY_CLASSNAME) && defined (JUCE_ANDROID_ACTIVITY_CLASSPATH))
#error "The JUCE_ANDROID_ACTIVITY_CLASSNAME and JUCE_ANDROID_ACTIVITY_CLASSPATH macros must be set!"
#endif
//==============================================================================
extern JNIEnv* getEnv() noexcept;
// You should rarely need to use this function. Only if you expect callbacks
// on a java thread which you did not create yourself.
extern void setEnv (JNIEnv* env) noexcept;
//==============================================================================
template <typename JavaType>
class LocalRef
{
public:
explicit inline LocalRef() noexcept : obj (0) {}
explicit inline LocalRef (JavaType o) noexcept : obj (o) {}
inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {}
inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); }
~LocalRef() { clear(); }
/* @internal */
extern JNIEnv* attachAndroidJNI() noexcept;
void clear()
{
if (obj != 0)
{
getEnv()->DeleteLocalRef (obj);
obj = 0;
}
}
LocalRef& operator= (const LocalRef& other)
{
JavaType newObj = retain (other.obj);
clear();
obj = newObj;
return *this;
}
LocalRef& operator= (LocalRef&& other)
{
clear();
std::swap (other.obj, obj);
return *this;
}
inline operator JavaType() const noexcept { return obj; }
inline JavaType get() const noexcept { return obj; }
private:
JavaType obj;
static JavaType retain (JavaType obj)
{
return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj);
}
};
//==============================================================================
class GlobalRef
{
public:
inline GlobalRef() noexcept : obj (0) {}
inline explicit GlobalRef (jobject o) : obj (retain (o)) {}
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {}
inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); }
inline GlobalRef() noexcept : obj (0) {}
inline explicit GlobalRef (const LocalRef<jobject>& o) : obj (retain (o.get(), getEnv())) {}
inline explicit GlobalRef (const LocalRef<jobject>& o, JNIEnv* env) : obj (retain (o.get(), env)) {}
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj, getEnv())) {}
inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); }
~GlobalRef() { clear(); }
inline void clear()
inline void clear() { if (obj != 0) clear (getEnv()); }
inline void clear (JNIEnv* env)
{
if (obj != 0)
{
getEnv()->DeleteGlobalRef (obj);
env->DeleteGlobalRef (obj);
obj = 0;
}
}
inline GlobalRef& operator= (const GlobalRef& other)
{
jobject newObj = retain (other.obj);
jobject newObj = retain (other.obj, getEnv());
clear();
obj = newObj;
return *this;
@@ -110,71 +149,33 @@ private:
//==============================================================================
jobject obj = 0;
static inline jobject retain (jobject obj)
static inline jobject retain (jobject obj, JNIEnv* env)
{
return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj);
return obj == 0 ? 0 : env->NewGlobalRef (obj);
}
};
//==============================================================================
template <typename JavaType>
class LocalRef
{
public:
explicit inline LocalRef() noexcept : obj (0) {}
explicit inline LocalRef (JavaType o) noexcept : obj (o) {}
inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {}
inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); }
~LocalRef() { clear(); }
void clear()
{
if (obj != 0)
{
getEnv()->DeleteLocalRef (obj);
obj = 0;
}
}
LocalRef& operator= (const LocalRef& other)
{
JavaType newObj = retain (other.obj);
clear();
obj = newObj;
return *this;
}
LocalRef& operator= (LocalRef&& other)
{
clear();
std::swap (other.obj, obj);
return *this;
}
inline operator JavaType() const noexcept { return obj; }
inline JavaType get() const noexcept { return obj; }
private:
JavaType obj;
static JavaType retain (JavaType obj)
{
return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj);
}
};
//==============================================================================
extern LocalRef<jobject> getAppContext() noexcept;
extern LocalRef<jobject> getCurrentActivity() noexcept;
extern LocalRef<jobject> getMainActivity() noexcept;
//==============================================================================
struct SystemJavaClassComparator;
class JNIClassBase
{
public:
explicit JNIClassBase (const char* classPath);
explicit JNIClassBase (const char* classPath, int minSDK, const void* byteCode, size_t byteCodeSize);
virtual ~JNIClassBase();
inline operator jclass() const noexcept { return classRef; }
inline operator jclass() const noexcept { return classRef; }
static void initialiseAllClasses (JNIEnv*);
static void releaseAllClasses (JNIEnv*);
inline const char* getClassPath() const noexcept { return classPath; }
protected:
virtual void initialiseFields (JNIEnv*) = 0;
@@ -182,172 +183,146 @@ protected:
jmethodID resolveStaticMethod (JNIEnv*, const char* methodName, const char* params);
jfieldID resolveField (JNIEnv*, const char* fieldName, const char* signature);
jfieldID resolveStaticField (JNIEnv*, const char* fieldName, const char* signature);
void resolveCallbacks (JNIEnv*, const Array<JNINativeMethod>&);
private:
friend struct SystemJavaClassComparator;
const char* const classPath;
jclass classRef;
const void* byteCode;
size_t byteCodeSize;
int minSDK;
jclass classRef = 0;
static Array<JNIClassBase*>& getClasses();
void initialise (JNIEnv*);
void release (JNIEnv*);
void tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader);
JUCE_DECLARE_NON_COPYABLE (JNIClassBase)
};
//==============================================================================
#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);
#define CREATE_JNI_STATICFIELD(fieldID, stringName, signature) fieldID = resolveStaticField (env, stringName, signature);
#define DECLARE_JNI_METHOD(methodID, stringName, params) jmethodID methodID;
#define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID;
#define DECLARE_JNI_CLASS(CppClassName, javaPath) \
#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);
#define CREATE_JNI_STATICFIELD(fieldID, stringName, signature) fieldID = resolveStaticField (env, stringName, signature);
#define CREATE_JNI_CALLBACK(callbackName, stringName, signature) callbacks.add ({stringName, signature, (void*) callbackName});
#define DECLARE_JNI_METHOD(methodID, stringName, params) jmethodID methodID;
#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) {} \
CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, byteCodeSize) {} \
\
void initialiseFields (JNIEnv* env) \
{ \
ignoreUnused (env); \
JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD); \
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) \
JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \
}; \
static CppClassName ## _Class CppClassName;
//==============================================================================
#if defined (__arm__)
#define JUCE_ARM_SOFT_FLOAT_ABI __attribute__ ((pcs("aapcs")))
#else
#define JUCE_ARM_SOFT_FLOAT_ABI
#endif
#define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \
extern "C" __attribute__ ((visibility("default"))) JUCE_ARM_SOFT_FLOAT_ABI returnType JUCE_JOIN_MACRO (JUCE_JOIN_MACRO (Java_, className), _ ## methodName) params
#define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \
DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr, 0)
//==============================================================================
class AndroidSystem
{
public:
AndroidSystem();
void initialise (JNIEnv*, jobject activity, jstring appFile, jstring appDataDir);
void shutdown (JNIEnv*);
//==============================================================================
GlobalRef activity;
String appFile, appDataDir;
int screenWidth, screenHeight, dpi;
};
extern AndroidSystem android;
#define DECLARE_JNI_CLASS(CppClassName, javaPath) \
DECLARE_JNI_CLASS_WITH_MIN_SDK (CppClassName, javaPath, 16)
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(JZ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
METHOD (finish, "finish", "()V") \
METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \
METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
METHOD (isPermissionDeclaredInManifestString, "isPermissionDeclaredInManifest", "(Ljava/lang/String;)Z") \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \
METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \
METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \
METHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \
METHOD (invocationHandlerContextDeleted, "invocationHandlerContextDeleted", "(Ljava/lang/reflect/InvocationHandler;)V") \
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \
METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \
METHOD (getApplicationContext, "getApplicationContext", "()Landroid/content/Context;") \
METHOD (getApplicationInfo, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;") \
METHOD (checkCallingOrSelfPermission, "checkCallingOrSelfPermission", "(Ljava/lang/String;)I") \
METHOD (getCacheDir, "getCacheDir", "()Ljava/io/File;")
DECLARE_JNI_CLASS (AndroidContext, "android/content/Context")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (finish, "finish", "()V") \
METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \
METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \
METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \
METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \
METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \
METHOD (addAppPausedResumedListener, "addAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") \
METHOD (removeAppPausedResumedListener, "removeAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V")
METHOD (getFragmentManager, "getFragmentManager", "()Landroid/app/FragmentManager;") \
METHOD (setContentView, "setContentView", "(Landroid/view/View;)V") \
METHOD (getWindow, "getWindow", "()Landroid/view/Window;")
DECLARE_JNI_CLASS (AndroidActivity, "android/app/Activity")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \
METHOD (setArguments, "setArguments", "(Landroid/os/Bundle;)V")
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH)
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidFragment, "android/app/Fragment", 11)
#undef JNI_CLASS_MEMBERS
//==============================================================================
#if __ANDROID_API__ >= 21
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (build, "build", "()Landroid/media/AudioAttributes;") \
METHOD (constructor, "<init>", "()V") \
METHOD (setContentType, "setContentType", "(I)Landroid/media/AudioAttributes$Builder;") \
METHOD (setUsage, "setUsage", "(I)Landroid/media/AudioAttributes$Builder;")
DECLARE_JNI_CLASS (AndroidAudioAttributesBuilder, "android/media/AudioAttributes$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidAudioAttributesBuilder, "android/media/AudioAttributes$Builder", 21)
#undef JNI_CLASS_MEMBERS
#endif
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (abandonAudioFocus, "abandonAudioFocus", "(Landroid/media/AudioManager$OnAudioFocusChangeListener;)I") \
METHOD (requestAudioFocus, "requestAudioFocus", "(Landroid/media/AudioManager$OnAudioFocusChangeListener;II)I")
DECLARE_JNI_CLASS (AndroidAudioManager, "android/media/AudioManager")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
STATICMETHOD (createBitmapFrom, "createBitmap", "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;") \
METHOD (compress, "compress", "(Landroid/graphics/Bitmap$CompressFormat;ILjava/io/OutputStream;)Z") \
METHOD (getHeight, "getHeight", "()I") \
METHOD (getWidth, "getWidth", "()I") \
METHOD (recycle, "recycle", "()V") \
METHOD (setPixel, "setPixel", "(III)V")
METHOD (setPixel, "setPixel", "(III)V") \
METHOD (getPixels, "getPixels", "([IIIIIII)V")
DECLARE_JNI_CLASS (AndroidBitmap, "android/graphics/Bitmap")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (valueOf, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;")
DECLARE_JNI_CLASS (AndroidBitmapConfig, "android/graphics/Bitmap$Config")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (decodeByteArray, "decodeByteArray", "([BII)Landroid/graphics/Bitmap;")
DECLARE_JNI_CLASS (AndroidBitmapFactory, "android/graphics/BitmapFactory")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (containsKey, "containsKey", "(Ljava/lang/String;)Z") \
METHOD (get, "get", "(Ljava/lang/String;)Ljava/lang/Object;") \
@@ -373,7 +348,7 @@ DECLARE_JNI_CLASS (AndroidBitmapFactory, "android/graphics/BitmapFactory")
DECLARE_JNI_CLASS (AndroidBundle, "android/os/Bundle")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (dumpReferenceTables, "dumpReferenceTables", "()V")
DECLARE_JNI_CLASS (AndroidDebug, "android/os/Debug")
@@ -381,13 +356,15 @@ DECLARE_JNI_CLASS (AndroidBundle, "android/os/Bundle")
#define JUCE_LOG_JNI_REFERENCES_TABLE getEnv()->CallStaticVoidMethod (AndroidDebug, AndroidDebug.dumpReferenceTables);
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getRotation, "getRotation", "()I")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getRotation, "getRotation", "()I") \
METHOD (getMetrics, "getMetrics", "(Landroid/util/DisplayMetrics;)V" ) \
METHOD (getSize, "getSize", "(Landroid/graphics/Point;)V" )
DECLARE_JNI_CLASS (AndroidDisplay, "android/view/Display")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithLooper, "<init>", "(Landroid/os/Looper;)V") \
METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \
@@ -396,22 +373,22 @@ DECLARE_JNI_CLASS (AndroidDisplay, "android/view/Display")
DECLARE_JNI_CLASS (AndroidHandler, "android/os/Handler")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (getLooper, "getLooper", "()Landroid/os/Looper;") \
METHOD (join, "join", "()V") \
METHOD (quitSafely, "quitSafely", "()Z") \
METHOD (start, "start", "()V")
DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (createChooser, "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;") \
METHOD (addCategory, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithContextAndClass, "<init>", "(Landroid/content/Context;Ljava/lang/Class;)V") \
METHOD (constructWithString, "<init>", "(Ljava/lang/String;)V") \
METHOD (constructWithUri, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V") \
METHOD (getAction, "getAction", "()Ljava/lang/String;") \
METHOD (getCategories, "getCategories", "()Ljava/util/Set;") \
METHOD (getData, "getData", "()Landroid/net/Uri;") \
@@ -432,23 +409,41 @@ DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread")
DECLARE_JNI_CLASS (AndroidIntent, "android/content/Intent")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (postRotate, "postRotate", "(FFF)Z") \
METHOD (postScale, "postScale", "(FFFF)Z") \
METHOD (postTranslate, "postTranslate", "(FF)Z") \
METHOD (setValues, "setValues", "([F)V")
METHOD (setValues, "setValues", "([F)V") \
METHOD (mapRect, "mapRect", "(Landroid/graphics/RectF;)Z")
DECLARE_JNI_CLASS (AndroidMatrix, "android/graphics/Matrix")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getPackageInfo, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getPackageInfo, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;") \
METHOD (resolveActivity, "resolveActivity", "(Landroid/content/Intent;I)Landroid/content/pm/ResolveInfo;") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z")
DECLARE_JNI_CLASS (AndroidPackageManager, "android/content/pm/PackageManager")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (requestedPermissions, "requestedPermissions", "[Ljava/lang/String;") \
FIELD (activities, "activities", "[Landroid/content/pm/ActivityInfo;") \
FIELD (providers, "providers", "[Landroid/content/pm/ProviderInfo;")
DECLARE_JNI_CLASS (AndroidPackageInfo, "android/content/pm/PackageInfo")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (name, "name", "Ljava/lang/String;") \
FIELD (packageName, "packageName", "Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidPackageItemInfo, "android/content/pm/PackageItemInfo")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(I)V") \
METHOD (setColor, "setColor", "(I)V") \
METHOD (setAlpha, "setAlpha", "(I)V") \
@@ -459,56 +454,76 @@ DECLARE_JNI_CLASS (AndroidPackageManager, "android/content/pm/PackageManager")
METHOD (getTextWidths, "getTextWidths", "(Ljava/lang/String;[F)I") \
METHOD (setTextScaleX, "setTextScaleX", "(F)V") \
METHOD (getTextPath, "getTextPath", "(Ljava/lang/String;IIFFLandroid/graphics/Path;)V") \
METHOD (getCharsPath, "getTextPath", "([CIIFFLandroid/graphics/Path;)V") \
METHOD (setShader, "setShader", "(Landroid/graphics/Shader;)Landroid/graphics/Shader;") \
DECLARE_JNI_CLASS (AndroidPaint, "android/graphics/Paint")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (create, "<init>", "(Landroid/graphics/Bitmap;)V") \
METHOD (setMatrix, "setMatrix", "(Landroid/graphics/Matrix;)V") \
METHOD (drawPath, "drawPath", "(Landroid/graphics/Path;Landroid/graphics/Paint;)V") \
METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \
METHOD (getClipBounds, "getClipBounds", "()Landroid/graphics/Rect;")
DECLARE_JNI_CLASS (AndroidCanvas, "android/graphics/Canvas")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getActivity, "getActivity", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;") \
METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;")
DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidRange, "android/util/Range")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidRange, "android/util/Range", 21)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (create, "<init>", "(II)V") \
FIELD (x, "x", "I") \
FIELD (y, "y", "I")
DECLARE_JNI_CLASS (AndroidPoint, "android/graphics/Point")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(IIII)V") \
FIELD (left, "left", "I") \
FIELD (right, "right", "I") \
FIELD (top, "top", "I") \
FIELD (bottom, "bottom", "I") \
FIELD (bottom, "bottom", "I")
DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I") \
METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;")
DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getHeight, "getHeight", "()I") \
METHOD (getWidth, "getWidth", "()I")
DECLARE_JNI_CLASS (AndroidSize, "android/util/Size")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidSize, "android/util/Size", 21)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (parse, "parse", "(Ljava/lang/String;)Landroid/net/Uri;") \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidUri, "android/net/Uri")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (construct, "<init>", "(Landroid/content/Context;)V") \
METHOD (layout, "layout", "(IIII)V") \
METHOD (getLeft, "getLeft", "()I") \
METHOD (getTop, "getTop", "()I") \
@@ -520,26 +535,30 @@ DECLARE_JNI_CLASS (AndroidUri, "android/net/Uri")
METHOD (requestFocus, "requestFocus", "()Z") \
METHOD (hasFocus, "hasFocus", "()Z") \
METHOD (invalidate, "invalidate", "(IIII)V") \
METHOD (setVisibility, "setVisibility", "(I)V")
METHOD (setVisibility, "setVisibility", "(I)V") \
METHOD (setLayoutParams, "setLayoutParams", "(Landroid/view/ViewGroup$LayoutParams;)V") \
METHOD (findViewById, "findViewById", "(I)Landroid/view/View;") \
METHOD (getRootView, "getRootView", "()Landroid/view/View;") \
METHOD (addOnLayoutChangeListener, "addOnLayoutChangeListener", "(Landroid/view/View$OnLayoutChangeListener;)V")
DECLARE_JNI_CLASS (AndroidView, "android/view/View")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addView, "addView", "(Landroid/view/View;)V") \
METHOD (removeView, "removeView", "(Landroid/view/View;)V")
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;")
DECLARE_JNI_CLASS (AndroidWindowManager, "android/view/WindowManager")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(I)V") \
METHOD (add, "add", "(Ljava/lang/Object;)Z") \
METHOD (iterator, "iterator", "()Ljava/util/Iterator;") \
@@ -549,27 +568,28 @@ DECLARE_JNI_CLASS (AndroidWindowManager, "android/view/WindowManager")
DECLARE_JNI_CLASS (JavaArrayList, "java/util/ArrayList")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (valueOf, "valueOf", "(Z)Ljava/lang/Boolean;") \
METHOD (booleanValue, "booleanValue", "()Z")
DECLARE_JNI_CLASS (JavaBoolean, "java/lang/Boolean")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (get, "get", "([B)Ljava/nio/ByteBuffer;") \
METHOD (remaining, "remaining", "()I")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (get, "get", "([B)Ljava/nio/ByteBuffer;") \
METHOD (remaining, "remaining", "()I") \
STATICMETHOD (wrap, "wrap", "([B)Ljava/nio/ByteBuffer;")
DECLARE_JNI_CLASS (JavaByteBuffer, "java/nio/ByteBuffer")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaCharSequence, "java/lang/CharSequence")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (forName, "forName", "(Ljava/lang/String;)Ljava/lang/Class;") \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
@@ -589,13 +609,13 @@ DECLARE_JNI_CLASS (JavaCharSequence, "java/lang/CharSequence")
DECLARE_JNI_CLASS (JavaClass, "java/lang/Class")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaEnum, "java/lang/Enum")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (getAbsolutePath, "getAbsolutePath", "()Ljava/lang/String;") \
METHOD (length, "length", "()J")
@@ -603,7 +623,7 @@ DECLARE_JNI_CLASS (JavaEnum, "java/lang/Enum")
DECLARE_JNI_CLASS (JavaFile, "java/io/File")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([B)I")
@@ -611,7 +631,7 @@ DECLARE_JNI_CLASS (JavaFile, "java/io/File")
DECLARE_JNI_CLASS (JavaFileInputStream, "java/io/FileInputStream")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (close, "close", "()V") \
METHOD (write, "write", "([BII)V")
@@ -619,14 +639,14 @@ DECLARE_JNI_CLASS (JavaFileInputStream, "java/io/FileInputStream")
DECLARE_JNI_CLASS (JavaFileOutputStream, "java/io/FileOutputStream")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithCapacity, "<init>", "(I)V")
DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (parseInt, "parseInt", "(Ljava/lang/String;I)I") \
STATICMETHOD (valueOf, "valueOf", "(I)Ljava/lang/Integer;") \
METHOD (intValue, "intValue", "()I")
@@ -634,27 +654,27 @@ DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap")
DECLARE_JNI_CLASS (JavaInteger, "java/lang/Integer")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (hasNext, "hasNext", "()Z") \
METHOD (next, "next", "()Ljava/lang/Object;")
DECLARE_JNI_CLASS (JavaIterator, "java/util/Iterator")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (get, "get", "(I)Ljava/lang/Object;") \
METHOD (size, "size", "()I")
DECLARE_JNI_CLASS (JavaList, "java/util/List")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V")
DECLARE_JNI_CLASS (JavaLong, "java/lang/Long")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (get, "get", "(Ljava/lang/Object;)Ljava/lang/Object;") \
METHOD (keySet, "keySet", "()Ljava/util/Set;") \
METHOD (put, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
@@ -662,7 +682,7 @@ DECLARE_JNI_CLASS (JavaLong, "java/lang/Long")
DECLARE_JNI_CLASS (JavaMap, "java/util/Map")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
METHOD (getParameterTypes, "getParameterTypes", "()[Ljava/lang/Class;") \
@@ -675,7 +695,7 @@ DECLARE_JNI_CLASS (JavaMethod, "java/lang/reflect/Method")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (getClass, "getClass", "()Ljava/lang/Class;") \
METHOD (toString, "toString", "()Ljava/lang/String;")
@@ -683,7 +703,7 @@ DECLARE_JNI_CLASS (JavaMethod, "java/lang/reflect/Method")
DECLARE_JNI_CLASS (JavaObject, "java/lang/Object")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (contains, "contains", "(Ljava/lang/Object;)Z") \
METHOD (iterator, "iterator", "()Ljava/util/Iterator;") \
METHOD (size, "size", "()I")
@@ -691,13 +711,45 @@ DECLARE_JNI_CLASS (JavaObject, "java/lang/Object")
DECLARE_JNI_CLASS (JavaSet, "java/util/Set")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (concat, "concat", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (getBytes, "getBytes", "()[B")
DECLARE_JNI_CLASS (JavaString, "java/lang/String")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK)
DECLARE_JNI_CLASS (AndroidBuild, "android/os/Build")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK)
DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (registerActivityLifecycleCallbacks, "registerActivityLifecycleCallbacks", "(Landroid/app/Application$ActivityLifecycleCallbacks;)V") \
METHOD (unregisterActivityLifecycleCallbacks, "unregisterActivityLifecycleCallbacks", "(Landroid/app/Application$ActivityLifecycleCallbacks;)V")
DECLARE_JNI_CLASS (AndroidApplication, "android/app/Application")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;)V") \
METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;")
DECLARE_JNI_CLASS (AndroidSurfaceView, "android/view/SurfaceView")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getSurface, "getSurface", "()Landroid/view/Surface;") \
METHOD (addCallback, "addCallback", "(Landroid/view/SurfaceHolder$Callback;)V") \
METHOD (removeCallback, "removeCallback", "(Landroid/view/SurfaceHolder$Callback;)V")
DECLARE_JNI_CLASS (AndroidSurfaceHolder, "android/view/SurfaceHolder")
#undef JNI_CLASS_MEMBERS
//==============================================================================
namespace
{
@@ -778,6 +830,10 @@ namespace
}
}
//==============================================================================
int getAndroidSDKVersion();
bool isPermissionDeclaredInManifest (const String& requestedPermission);
//==============================================================================
class AndroidInterfaceImplementer;
@@ -789,8 +845,8 @@ LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
LocalRef<jobject> subclass);
//==============================================================================
jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
void juce_dispatchDelete (JNIEnv*, jlong);
jobject juce_invokeImplementer (JNIEnv*, jobject, jlong, jobject, jobject, jobjectArray);
void juce_dispatchDelete (JNIEnv*, jobject, jlong);
//==============================================================================
class AndroidInterfaceImplementer
@@ -798,11 +854,12 @@ class AndroidInterfaceImplementer
protected:
virtual ~AndroidInterfaceImplementer();
virtual jobject invoke (jobject proxy, jobject method, jobjectArray args);
void clear();
//==============================================================================
friend LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef<jobject>);
friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
friend void juce_dispatchDelete (JNIEnv*, jlong);
friend jobject juce_invokeImplementer (JNIEnv*, jobject, jlong, jobject, jobject, jobjectArray);
friend void juce_dispatchDelete (JNIEnv*, jobject, jlong);
private:
GlobalRef javaSubClass;
GlobalRef invocationHandler;
@@ -814,23 +871,105 @@ LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName);
//==============================================================================
class AppPausedResumedListener : public AndroidInterfaceImplementer
class ActivityLifecycleCallbacks : public AndroidInterfaceImplementer
{
public:
struct Owner
virtual void onActivityCreated (jobject /*activity*/, jobject /*bundle*/) {}
virtual void onActivityDestroyed (jobject /*activity*/) {}
virtual void onActivityPaused (jobject /*activity*/) {}
virtual void onActivityResumed (jobject /*activity*/) {}
virtual void onActivitySaveInstanceState (jobject /*activity*/, jobject /*bundle*/) {}
virtual void onActivityStarted (jobject /*activity*/) {}
virtual void onActivityStopped (jobject /*activity*/) {}
private:
jobject invoke (jobject, jobject, jobjectArray) override;
};
//==============================================================================
struct SurfaceHolderCallback : AndroidInterfaceImplementer
{
virtual ~SurfaceHolderCallback() {}
virtual void surfaceChanged (LocalRef<jobject> holder, int format, int width, int height) = 0;
virtual void surfaceCreated (LocalRef<jobject> holder) = 0;
virtual void surfaceDestroyed (LocalRef<jobject> holder) = 0;
private:
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
{
virtual ~Owner() {}
auto* env = getEnv();
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
LocalRef<jobject> holder (env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr);
if (methodName == "surfaceChanged")
{
int intArgs[3];
virtual void appPaused() = 0;
virtual void appResumed() = 0;
};
for (int i = 0; i < 3; ++i)
{
LocalRef<jobject> boxedType (env->GetObjectArrayElement (args, 1 + i));
intArgs[i] = env->CallIntMethod (boxedType.get(), JavaInteger.intValue);
}
AppPausedResumedListener (Owner&);
surfaceChanged (std::move (holder), intArgs[0], intArgs[1], intArgs[2]);
}
else if (methodName == "surfaceCreated")
{
surfaceCreated (std::move (holder));
}
else if (methodName == "surfaceDestroyed")
{
surfaceDestroyed (std::move (holder));
}
else
{
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
return nullptr;
}
};
//==============================================================================
class FragmentOverlay
{
public:
FragmentOverlay();
virtual ~FragmentOverlay();
void open();
jobject invoke (jobject proxy, jobject method, jobjectArray args) override;
virtual void onCreated (LocalRef<jobject> /*bundle*/) {}
virtual void onStart() {}
virtual void onRequestPermissionsResult (int /*requestCode*/,
const StringArray& /*permissions*/,
const Array<int>& /*grantResults*/) {}
virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef<jobject> /*data*/) {}
protected:
jobject getNativeHandle();
private:
Owner& owner;
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);
};
//==============================================================================
// Allows you to start an activity without requiring to have an activity
void startAndroidActivityForResult (const LocalRef<jobject>& intent, int requestCode,
std::function<void (int, int, LocalRef<jobject>)> && callback);
//==============================================================================
bool androidHasSystemFeature (const String& property);
String audioManagerGetProperty (const String& property);
} // namespace juce

+ 168
- 6
modules/juce_core/native/juce_android_Network.cpp View File

@@ -23,7 +23,168 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/JuceHTTPStream.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaJuceHttpStream[] =
{31,139,8,8,96,105,229,91,0,3,74,117,99,101,72,84,84,80,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124,84,213,157,
199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,32,40,146,128,200,75,32,65,17,6,20,146,181,29,
50,23,50,48,220,9,51,119,32,88,84,64,171,212,218,74,173,86,250,82,139,88,105,87,91,171,237,214,86,187,109,197,118,219,
218,90,109,245,179,174,187,125,91,119,237,167,187,186,218,118,221,246,227,102,127,231,49,153,137,77,33,124,239,255,127,
254,231,127,206,61,143,255,121,204,36,41,123,34,208,119,193,18,58,123,73,224,169,145,163,171,22,29,152,124,110,71,248,
214,145,193,207,157,62,245,139,197,215,175,242,156,233,36,26,39,162,137,237,139,35,164,254,253,114,14,209,11,36,237,139,
192,183,116,162,56,228,9,15,81,61,228,147,38,209,149,144,71,188,68,200,34,79,128,104,101,19,81,10,242,205,90,162,63,130,
183,193,95,192,255,1,173,142,200,0,33,80,5,162,160,27,92,12,54,131,173,224,42,48,2,118,1,27,92,11,174,3,199,192,205,224,
86,112,59,56,5,206,128,179,224,41,240,10,104,140,18,45,3,215,128,27,193,253,224,187,224,183,128,161,193,49,112,17,184,28,
236,6,71,193,221,224,33,240,44,120,5,188,13,194,13,68,237,96,0,92,5,246,129,227,224,62,240,8,248,1,248,37,120,19,248,27,
137,174,6,105,112,45,248,4,120,2,188,6,106,102,161,14,112,13,56,2,62,9,254,5,76,130,30,140,211,213,96,20,236,6,123,129,3,
242,224,125,224,40,184,13,156,4,31,5,119,131,79,130,123,193,253,224,113,240,36,248,30,120,17,252,28,188,10,94,7,255,3,
222,1,70,51,81,16,68,65,12,244,128,53,96,59,24,3,215,130,219,192,167,193,3,224,9,112,14,252,24,188,10,254,2,204,22,244,
17,88,160,17,116,128,249,96,17,184,8,108,2,127,7,28,112,61,184,25,220,13,206,128,175,128,239,128,31,129,127,6,175,129,55,
193,36,240,181,18,213,129,78,208,15,46,3,27,193,86,240,94,176,23,28,2,71,193,9,112,23,184,23,60,0,30,6,143,129,175,129,
103,193,207,192,239,192,155,224,207,128,98,232,59,168,4,115,64,63,216,14,118,131,125,96,28,28,4,71,192,205,224,36,184,23,
60,0,190,0,190,9,94,4,63,3,191,6,255,14,254,12,188,109,68,179,65,31,88,9,182,128,221,96,63,112,193,17,112,2,124,28,60,8,
190,8,190,14,190,15,126,14,222,2,122,59,226,2,52,130,118,208,11,150,131,203,64,2,100,64,1,92,7,110,5,31,1,167,64,16,221,
178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229,75,88,214,52,23,116,129,110,48,15,
204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126,48,0,86,128,75,192,74,112,
41,88,5,214,128,117,224,50,176,30,108,4,91,192,54,176,29,92,69,178,31,197,127,33,37,151,98,111,8,43,125,101,153,190,30,
122,165,210,55,215,202,254,51,149,230,251,143,1,70,96,143,148,213,203,245,98,249,6,165,187,202,167,88,87,173,242,91,170,
236,181,202,94,173,244,35,202,30,45,179,71,149,189,70,233,55,65,175,83,250,109,202,94,175,236,181,74,95,170,244,134,50,
157,207,219,157,181,178,28,215,63,165,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,67,170,126,
62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,114,133,210,191,12,125,72,233,79,150,233,125,101,58,111,255,
38,165,63,13,125,179,210,249,248,95,174,244,31,150,249,252,91,173,60,27,122,213,248,23,235,121,165,86,198,196,2,213,158,
173,74,255,61,236,9,165,187,170,47,11,213,123,117,204,244,247,136,203,5,244,28,164,7,35,103,11,89,75,123,132,236,161,180,
144,97,250,176,144,221,244,77,226,241,209,68,41,33,165,159,161,252,12,140,88,65,200,249,116,147,144,81,186,69,201,15,8,
41,235,49,240,190,59,72,198,217,105,33,25,125,78,200,56,125,94,200,122,250,130,144,125,244,13,33,139,239,197,154,87,242,
135,64,163,42,186,75,180,191,141,76,33,189,244,30,33,131,66,122,208,30,83,200,86,250,146,40,215,41,210,188,221,59,84,59,
71,133,172,163,221,66,154,52,166,236,7,132,244,211,97,33,13,186,65,201,99,42,255,164,144,58,61,36,100,43,61,172,198,225,
31,136,175,153,118,241,158,16,118,11,46,195,200,79,10,233,17,254,149,72,31,18,114,30,253,68,196,93,5,237,83,241,119,187,
136,189,86,81,46,138,113,217,165,228,135,72,198,246,199,132,92,64,95,23,178,146,190,45,164,37,100,61,86,212,136,144,17,
218,47,164,44,87,143,145,146,82,150,175,87,254,13,234,61,13,88,101,35,66,134,233,25,226,123,225,108,97,111,68,254,19,196,
215,83,43,57,66,250,232,90,33,27,233,125,42,125,68,200,0,93,79,114,221,29,21,50,74,199,133,140,209,99,66,118,211,151,149,
252,138,178,127,85,200,14,122,92,200,57,244,53,226,107,85,142,87,51,118,85,41,235,233,140,144,178,93,173,24,247,247,11,
25,164,123,136,239,205,33,154,32,190,63,7,233,58,37,111,36,190,158,103,145,75,124,45,55,208,157,196,215,113,51,125,139,
248,94,221,66,25,33,155,232,227,196,215,116,47,189,87,200,160,168,103,190,26,143,249,248,145,233,110,122,148,248,158,46,
237,92,222,45,228,124,122,86,165,127,76,242,142,70,36,215,22,223,35,170,32,31,199,198,117,229,28,105,247,148,229,247,169,
252,151,144,191,87,229,243,120,102,84,218,47,121,254,219,200,63,174,242,121,253,63,197,193,243,50,248,77,135,244,253,131,
146,239,116,240,117,128,251,4,124,67,115,164,45,170,100,167,146,151,40,185,126,14,175,75,23,250,167,176,233,249,32,135,
145,24,209,24,141,91,179,68,132,242,92,94,223,153,118,185,71,182,179,32,109,139,49,156,83,22,37,98,68,7,44,175,136,112,
199,234,19,114,60,86,141,18,85,108,122,222,2,33,187,222,228,109,99,226,125,15,183,203,126,58,22,183,4,69,31,77,252,240,
188,71,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,71,237,242,60,76,
244,121,80,23,223,105,3,56,63,107,41,177,80,167,102,150,232,51,209,82,31,113,25,137,228,98,235,17,35,45,76,250,94,46,74,
105,200,247,10,233,88,189,194,98,40,139,1,75,237,84,205,155,197,104,132,80,122,46,158,37,91,215,159,144,90,40,83,76,156,
16,132,213,171,3,46,153,58,187,116,113,206,243,158,47,17,82,71,219,47,184,33,116,225,114,172,204,0,241,81,122,165,93,238,
221,188,207,62,140,114,15,180,77,240,28,62,230,71,170,86,216,121,138,231,205,71,106,163,72,85,96,95,226,55,129,32,59,96,
93,198,199,145,13,31,11,208,200,173,149,52,252,193,8,13,223,22,164,29,31,170,163,225,15,215,208,240,237,213,20,249,239,
29,199,162,120,179,69,59,142,90,120,75,21,13,31,13,209,182,27,43,41,113,83,132,18,239,15,210,22,92,253,19,183,132,200,
123,204,123,199,65,175,95,213,232,69,207,53,117,35,168,154,45,227,47,129,185,173,18,177,233,21,49,82,15,123,158,199,135,
175,25,49,176,12,235,216,177,6,240,142,160,175,213,215,68,222,155,90,61,77,20,241,57,177,11,113,18,56,177,197,244,7,60,
47,194,179,209,119,53,180,86,156,3,65,45,162,183,117,45,185,49,78,235,124,154,222,232,215,97,111,166,83,20,48,151,155,
237,194,230,224,114,236,163,128,111,201,205,141,34,29,241,59,184,34,158,50,131,140,167,94,242,155,204,137,197,80,34,232,
229,158,94,120,46,247,97,22,251,46,166,26,239,75,186,206,186,158,79,156,13,161,214,165,104,195,210,96,132,34,245,78,108,
9,215,67,60,70,23,34,166,130,134,131,15,47,47,34,21,69,108,85,121,66,34,42,60,232,85,136,26,253,126,244,172,6,245,207,
245,201,59,206,85,124,70,217,172,61,213,56,23,2,208,15,205,150,247,139,254,112,45,69,194,237,168,193,251,25,246,168,247,
156,247,5,246,91,239,159,124,94,139,52,95,21,249,252,17,58,24,48,197,216,93,28,254,175,201,234,112,95,161,235,119,33,22,
161,174,183,229,216,86,201,122,27,43,68,140,135,233,19,179,249,12,227,126,228,59,201,34,53,17,61,241,128,159,154,141,196,
233,10,180,110,16,30,1,109,41,214,71,139,193,22,85,106,145,200,120,44,136,115,36,168,13,159,14,83,226,116,13,69,204,196,
131,94,120,46,226,35,226,29,244,106,166,180,174,209,52,115,241,241,159,82,68,155,94,130,251,198,81,107,80,203,89,171,149,
92,195,165,153,179,86,96,133,242,183,61,77,57,235,18,232,65,214,202,218,160,95,202,35,5,246,55,38,115,214,42,165,255,110,
50,241,96,53,70,174,1,209,218,133,121,43,122,181,106,81,204,65,55,118,221,160,214,232,243,8,253,56,226,164,209,175,81,
177,116,171,86,67,7,98,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185,209,27,128,87,19,229,208,
206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,88,13,178,14,171,81,140,225,142,211,81,234,186,61,36,198,177,
155,70,38,67,106,44,22,137,220,37,199,219,228,104,122,18,159,149,86,140,132,57,104,106,30,233,213,11,175,153,243,101,125,
139,39,103,158,139,110,90,54,25,210,90,140,185,90,72,75,156,249,107,15,143,57,247,93,115,218,77,113,148,232,166,158,73,
212,188,170,155,188,147,124,255,240,99,87,170,22,123,168,87,220,131,121,236,116,136,61,171,90,124,214,184,1,233,199,196,
222,19,18,103,44,63,243,159,83,126,255,170,236,191,80,233,255,84,233,55,196,174,205,232,29,81,111,53,249,152,180,27,55,
132,30,101,191,103,55,116,126,149,189,206,232,247,140,94,103,215,119,190,95,19,70,47,198,129,159,41,175,206,150,159,119,
34,90,98,171,143,154,245,196,149,50,90,25,5,216,82,134,104,213,189,109,56,37,174,12,80,59,246,244,241,62,70,9,51,23,91,
139,207,56,216,113,174,172,69,126,2,207,156,181,65,236,65,173,44,76,93,175,133,88,139,62,151,97,119,106,235,250,141,120,
254,82,158,165,13,240,225,235,206,135,247,206,85,251,172,70,157,90,117,247,156,158,26,232,94,62,23,24,12,126,103,168,198,
135,155,72,232,32,171,64,127,2,214,197,150,33,44,149,214,75,161,16,243,193,182,133,89,116,97,243,255,78,114,61,193,48,31,
250,154,42,166,87,179,94,198,111,151,139,172,26,170,102,11,148,142,177,101,113,165,195,159,93,32,244,11,172,151,85,233,
106,106,212,55,32,26,59,112,195,14,176,151,194,44,80,178,35,150,251,230,208,194,112,160,242,162,215,159,82,254,124,222,
47,16,253,117,172,229,92,250,139,254,78,223,92,250,85,101,176,162,184,187,158,162,246,32,180,190,126,186,173,130,251,6,
17,27,139,238,184,103,234,189,17,93,104,30,148,180,58,49,70,1,143,211,55,155,22,122,132,21,177,226,195,213,178,57,34,189,
203,231,165,82,140,70,75,164,155,38,38,49,218,17,140,118,128,91,186,105,188,172,141,189,240,230,169,109,122,37,201,247,
68,164,52,130,66,110,49,235,100,218,27,66,204,84,83,241,36,225,182,97,212,192,235,95,57,41,107,230,250,170,73,18,247,243,
185,104,233,205,106,255,187,91,204,163,135,62,13,233,133,198,239,125,124,247,127,74,165,117,186,11,91,228,41,118,195,172,
239,176,187,24,169,91,12,209,137,14,185,135,110,141,85,137,207,177,69,251,135,59,100,172,108,137,69,113,103,183,166,206,
179,59,213,125,41,130,29,221,15,111,15,126,62,209,33,63,27,226,76,220,133,157,86,75,36,195,180,148,249,137,203,136,238,
172,172,163,3,55,6,168,69,91,142,246,108,221,85,69,199,60,79,172,218,9,89,73,24,51,13,99,198,186,222,98,36,239,86,30,181,
134,26,85,92,206,154,58,255,27,88,168,137,199,44,67,116,106,244,88,135,252,238,160,29,37,18,7,112,255,200,97,47,206,227,
157,46,110,47,133,90,58,104,242,154,18,57,209,158,3,21,20,169,118,98,67,136,171,8,27,134,119,139,214,110,54,34,50,230,
163,46,199,234,33,126,67,146,109,137,136,27,17,19,223,79,48,241,51,7,111,144,55,212,103,202,250,206,191,75,228,255,52,42,
73,55,32,199,242,8,228,77,1,121,103,44,126,47,192,207,175,147,1,57,166,167,32,239,83,249,229,119,95,158,111,170,122,252,
74,242,239,15,206,42,223,14,85,95,157,146,81,245,222,162,140,171,250,226,170,78,63,201,207,58,113,225,209,39,62,191,68,
85,217,5,101,109,43,239,131,69,53,194,110,168,54,151,202,71,133,125,158,242,227,159,65,25,201,123,167,108,67,84,148,105,
130,165,7,189,233,86,246,102,85,46,78,197,241,96,24,222,46,194,133,141,45,35,109,89,140,216,0,153,3,105,39,237,174,32,
109,69,63,121,86,244,119,111,39,107,117,214,113,236,81,55,157,117,98,118,46,151,205,81,24,22,215,118,220,222,33,219,217,
227,142,81,237,154,116,126,116,202,105,75,193,113,146,187,50,54,177,13,164,109,24,34,125,195,208,6,242,224,129,253,112,
35,85,111,44,140,218,235,19,137,205,91,221,156,157,220,191,96,111,242,96,146,216,16,105,112,210,185,143,54,132,34,67,120,
120,135,134,118,14,13,161,130,128,82,184,174,13,237,164,250,161,164,147,202,101,211,169,184,107,79,184,241,4,30,219,220,
116,38,223,79,177,161,209,236,254,120,46,155,73,199,247,226,53,241,233,239,234,88,216,79,139,207,239,49,99,71,250,169,
229,188,165,250,169,125,40,149,204,28,76,239,139,39,29,39,235,38,121,225,248,90,103,52,147,205,167,157,61,171,51,201,60,
218,54,251,124,62,155,108,119,44,155,226,47,250,107,167,13,104,79,78,85,210,54,67,254,38,123,255,46,229,96,195,165,121,6,
151,173,233,61,78,210,45,228,208,149,198,25,178,19,99,185,236,33,81,148,207,70,60,157,141,175,42,236,222,109,231,236,212,
6,103,188,224,22,123,89,59,149,189,225,138,181,19,163,246,56,47,60,205,92,238,93,55,101,190,162,224,150,217,235,165,61,
147,116,246,196,87,143,37,115,91,237,3,5,219,25,181,167,42,18,57,101,245,87,151,153,55,32,238,246,216,57,62,211,211,141,
185,92,97,220,181,83,101,197,106,202,61,224,32,103,50,82,102,189,98,215,94,76,244,116,207,210,156,151,123,162,237,152,
164,233,109,151,54,57,80,253,212,48,67,78,58,147,226,89,229,21,97,164,237,100,106,122,87,197,232,203,119,54,73,179,99,
187,241,245,174,59,190,109,203,80,105,237,245,83,184,148,139,156,169,214,168,116,185,167,234,81,1,171,66,246,221,205,162,
33,86,153,117,40,157,119,167,154,33,44,155,146,227,29,107,29,55,119,184,159,54,205,100,30,248,235,241,120,87,125,51,120,
172,192,15,85,78,175,110,186,97,171,237,242,176,47,25,176,254,70,11,185,28,182,151,248,234,100,38,35,118,146,214,243,231,
247,83,207,223,114,64,96,193,135,143,74,89,104,116,207,236,189,118,194,30,45,188,203,181,243,124,174,89,4,111,238,96,154,
199,110,236,252,126,249,169,185,125,183,199,186,2,95,151,197,165,55,115,110,63,13,156,47,123,224,188,203,22,19,208,49,
115,105,25,140,235,146,163,104,32,102,125,254,204,94,136,157,253,233,209,248,165,66,172,202,102,51,118,18,227,50,111,102,
231,76,118,116,95,62,190,197,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,118,210,182,99,143,223,142,61,126,59,
246,120,19,15,190,215,35,177,147,2,219,203,246,249,237,59,137,237,36,109,231,70,0,57,188,10,108,160,234,225,25,86,165,54,
226,80,32,57,58,106,231,243,29,125,125,125,84,33,245,117,153,228,158,60,121,147,169,84,14,41,50,147,227,227,182,147,34,
239,174,100,222,222,150,203,144,185,75,140,22,121,70,17,70,100,142,138,88,33,131,239,200,54,249,177,225,143,39,115,118,
34,75,94,117,34,80,160,116,52,80,93,73,79,100,75,167,6,89,163,24,83,215,46,45,198,162,69,78,21,31,144,162,165,116,128,20,
45,50,37,124,42,213,136,22,135,142,204,148,61,154,77,217,84,147,178,119,39,11,25,119,218,228,241,220,140,237,218,20,72,
149,154,82,155,154,241,84,174,158,102,150,213,144,63,149,85,77,38,102,147,193,167,239,48,249,132,192,2,133,166,226,152,
140,221,105,59,147,130,200,20,242,99,164,239,65,102,13,30,197,133,134,23,168,46,133,97,93,203,111,11,42,93,137,244,122,
188,202,206,173,227,53,228,133,67,89,156,146,137,244,160,125,88,56,150,159,24,84,1,195,102,156,146,98,24,120,238,22,59,
63,158,117,242,24,100,140,7,175,38,129,131,44,163,238,34,188,26,236,134,228,131,220,158,204,20,108,178,198,146,249,85,
136,72,213,70,27,33,0,203,229,184,53,80,197,152,104,209,80,218,177,17,41,50,145,167,160,82,18,217,109,136,132,240,24,118,
226,45,252,144,202,187,171,247,167,168,122,122,90,58,5,184,81,181,151,165,201,155,118,82,246,196,21,187,169,34,93,214,67,
95,218,81,77,170,72,231,215,78,140,37,11,121,151,183,38,157,23,227,64,102,58,143,126,186,60,151,75,89,179,47,173,246,111,
242,236,205,166,17,7,25,217,79,15,95,106,228,113,146,251,49,165,142,125,104,117,114,116,204,78,201,233,220,140,85,74,33,
110,44,133,97,5,146,165,96,66,2,175,16,146,47,5,143,195,71,67,119,10,104,36,30,171,14,187,24,143,26,104,235,178,153,76,
246,144,157,218,98,167,210,57,212,36,173,83,169,68,86,58,80,195,76,86,53,126,89,44,187,178,134,232,89,116,85,31,199,219,
61,120,44,20,207,69,228,27,47,206,48,215,220,53,73,55,73,161,162,38,43,242,136,72,245,230,16,233,88,193,84,153,83,97,176,
94,77,91,205,187,12,178,148,9,43,150,11,233,185,2,222,157,71,188,86,225,161,218,147,72,239,183,121,115,42,96,90,147,149,
81,71,141,121,30,152,121,151,135,139,236,72,169,247,220,113,42,22,195,121,30,139,201,84,177,22,75,164,69,96,200,251,28,
69,74,150,205,57,140,67,206,61,76,70,126,60,147,118,33,220,100,14,115,14,225,22,242,20,144,82,132,116,101,73,151,125,8,
230,69,0,169,163,194,204,23,118,237,71,13,126,200,188,216,2,41,228,142,149,111,57,213,60,249,238,93,167,100,44,219,102,
194,48,150,47,194,74,164,183,150,191,204,227,142,165,17,39,252,217,209,71,126,23,93,197,48,109,194,242,157,82,85,156,186,
89,185,31,83,133,91,182,28,235,248,57,144,223,157,205,237,183,83,151,151,69,160,23,59,139,104,129,89,112,68,36,87,28,76,
102,58,138,43,193,56,40,150,137,113,40,135,232,39,109,162,143,142,104,223,103,228,13,211,81,254,156,92,54,64,71,120,234,
30,205,56,199,190,203,190,195,158,198,231,109,227,159,216,188,117,222,240,64,47,255,71,159,215,224,112,98,249,96,186,117,
100,112,120,240,154,97,109,188,121,109,219,57,170,243,143,220,201,30,98,31,96,207,176,111,178,71,216,25,246,65,230,13,
107,127,209,250,39,38,14,107,199,175,59,35,10,247,14,12,14,177,72,37,89,236,5,228,210,106,115,228,163,236,239,217,9,246,
3,225,253,138,238,249,36,107,30,28,184,100,159,174,221,194,22,49,166,235,31,98,108,249,9,221,124,144,177,207,78,232,236,
80,253,9,221,251,19,86,159,214,14,246,27,204,240,106,225,126,195,28,94,48,180,96,80,55,62,203,172,129,75,12,163,197,208,
12,189,85,167,124,211,0,205,243,79,85,255,143,236,139,236,126,118,43,94,210,59,135,85,69,209,230,94,173,162,191,141,126,
141,94,111,92,126,139,214,219,163,237,104,214,42,14,61,62,113,61,107,180,60,204,109,239,127,154,53,84,178,250,208,138,95,
177,250,106,131,241,42,195,56,52,13,226,218,220,19,95,190,230,71,125,90,203,243,6,105,187,250,207,117,124,76,179,250,233,
32,187,131,121,155,81,89,109,143,182,191,249,229,182,143,172,68,15,40,95,211,218,98,144,208,58,91,233,78,62,202,189,186,
245,37,22,111,211,67,167,25,27,234,213,190,77,61,134,165,93,218,108,90,31,56,172,87,125,131,49,86,95,117,78,15,159,69,
159,245,202,207,49,182,224,156,238,223,87,255,65,189,226,83,172,126,167,238,187,166,101,167,30,188,135,181,12,233,218,
179,172,62,206,44,235,240,3,90,104,135,97,173,68,215,125,70,176,85,15,228,155,52,103,163,17,48,53,211,103,6,77,235,125,
172,165,246,162,86,51,64,111,241,169,163,183,197,243,29,241,252,185,166,189,231,51,24,149,54,157,238,195,80,15,78,180,
247,238,213,38,154,7,232,25,158,253,71,157,198,141,229,123,7,90,119,234,230,129,89,3,13,134,73,199,117,246,105,62,117,
154,151,61,207,26,131,154,79,219,233,161,83,172,69,11,32,221,100,68,123,163,203,163,70,180,35,234,213,42,184,1,3,88,84,
170,52,19,74,189,33,138,248,78,177,104,133,44,227,137,174,86,37,68,210,31,101,209,197,209,206,232,122,84,37,51,124,154,
95,214,224,41,86,213,73,26,211,232,100,140,225,255,209,163,158,71,106,53,246,92,45,251,118,236,190,58,50,24,211,144,41,
126,200,115,236,168,231,201,58,131,253,71,29,85,153,94,175,198,34,226,71,228,49,109,218,143,72,155,240,127,164,158,157,
136,189,212,160,69,222,104,96,13,127,152,197,34,63,108,98,145,163,237,190,170,147,179,141,170,183,59,89,213,201,185,172,
234,44,120,18,60,210,197,170,206,116,139,239,148,168,236,187,6,46,139,127,187,197,191,31,40,254,253,86,241,123,11,254,55,
92,252,59,146,226,223,113,241,239,20,138,127,203,101,82,233,239,185,116,75,254,254,141,127,15,195,98,242,119,191,143,240,
239,79,98,210,135,255,254,144,89,165,223,41,106,49,249,94,254,247,95,186,242,231,191,195,243,196,72,252,238,137,255,126,
144,84,89,241,123,71,75,182,149,255,173,217,255,3,11,139,181,139,164,38,0,0};
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (toString, "toString", "()Ljava/lang/String;") \
@@ -31,7 +192,8 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/roli/juce/JuceHTTPStream;") \
METHOD (connect, "connect", "()Z") \
METHOD (release, "release", "()V") \
METHOD (read, "read", "([BI)I") \
@@ -40,11 +202,11 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
METHOD (isExhausted, "isExhausted", "()Z") \
METHOD (setPosition, "setPosition", "(J)Z") \
DECLARE_JNI_CLASS (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream")
DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/roli/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream))
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([BII)I") \
@@ -192,8 +354,8 @@ public:
const ScopedLock lock (createStreamLock);
if (! hasBeenCancelled)
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.createHTTPStream,
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (HTTPStream,
HTTPStream.createHTTPStream,
javaString (address).get(),
(jboolean) isPost,
postDataArray,


+ 200
- 27
modules/juce_core/native/juce_android_RuntimePermissions.cpp View File

@@ -23,33 +23,185 @@
namespace juce
{
static void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr)
//==============================================================================
static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission)
{
if (callbackPtr == nullptr)
switch (permission)
{
// got a nullptr passed in from java! this should never happen...
jassertfalse;
return;
case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO";
case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_COARSE_LOCATION";
case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE";
case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE";
case RuntimePermissions::camera: return "android.permission.CAMERA";
}
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr);
if (RuntimePermissions::Callback callbackObj = *uptr)
callbackObj (permissionWasGranted);
// invalid permission
jassertfalse;
return {};
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME,
androidRuntimePermissionsCallback,
void, (JNIEnv* env, jobject, jboolean permissionsGranted, jlong callbackPtr))
static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission)
{
setEnv (env);
handleAndroidCallback (permissionsGranted != 0,
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr));
if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio;
else if (permission == "android.permission.ACCESS_COARSE_LOCATION") return RuntimePermissions::bluetoothMidi;
else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage;
else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage;
else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera;
return static_cast<RuntimePermissions::PermissionID> (-1);
}
//==============================================================================
struct PermissionsRequest
{
PermissionsRequest() {}
// using "= default" on the following method triggers an internal compiler error
// in Android NDK 17
PermissionsRequest (const PermissionsRequest& o)
: callback (o.callback), permission (o.permission)
{}
PermissionsRequest (PermissionsRequest&& o)
: callback (std::move (o.callback)), permission (o.permission)
{
o.permission = static_cast<RuntimePermissions::PermissionID> (-1);
}
PermissionsRequest (RuntimePermissions::Callback && callbackToUse,
RuntimePermissions::PermissionID permissionToRequest)
: callback (std::move (callbackToUse)), permission (permissionToRequest)
{}
PermissionsRequest& operator= (const PermissionsRequest & o)
{
callback = o.callback;
permission = o.permission;
return *this;
}
PermissionsRequest& operator= (PermissionsRequest && o)
{
callback = std::move (o.callback);
permission = o.permission;
return *this;
}
RuntimePermissions::Callback callback;
RuntimePermissions::PermissionID permission;
};
//==============================================================================
struct PermissionsOverlay : FragmentOverlay
{
PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {}
~PermissionsOverlay() {}
struct PermissionResult
{
PermissionsRequest request;
bool granted;
};
void onStart() override { onRequestPermissionsResult (0, {}, {}); }
void onRequestPermissionsResult (int /*requestCode*/,
const StringArray& permissions,
const Array<int>& grantResults) override
{
std::vector<PermissionResult> results;
{
ScopedLock lock (overlayGuard);
for (auto it = requests.begin(); it != requests.end();)
{
auto& request = *it;
if (RuntimePermissions::isGranted (request.permission))
{
results.push_back ({std::move (request), true});
it = requests.erase (it);
}
else
{
++it;
}
}
auto n = permissions.size();
for (int i = 0; i < n; ++i)
{
auto permission = androidPermissionToJucePermission (permissions[i]);
auto granted = (grantResults.getReference (i) == 0);
for (auto it = requests.begin(); it != requests.end();)
{
auto& request = *it;
if (request.permission == permission)
{
results.push_back ({std::move (request), granted});
it = requests.erase (it);
}
else
{
++it;
}
}
}
}
for (const auto& result : results)
if (result.request.callback)
result.request.callback (result.granted);
{
auto* env = getEnv();
ScopedLock lock (overlayGuard);
if (requests.size() > 0)
{
auto &request = requests.front();
StringArray permissionsArray{
jucePermissionToAndroidPermission (request.permission)};
auto jPermissionsArray = juceStringArrayToJava (permissionsArray);
auto requestPermissionsMethodID
= env->GetMethodID(AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V");
// this code should only be reached for SDKs >= 23, so this method should be
// be available
jassert(requestPermissionsMethodID != 0);
env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0);
}
else
{
getSingleton() = nullptr;
}
}
}
static std::unique_ptr<PermissionsOverlay>& getSingleton()
{
static std::unique_ptr<PermissionsOverlay> instance;
return instance;
}
CriticalSection& overlayGuard;
std::vector<PermissionsRequest> requests;
};
//==============================================================================
void RuntimePermissions::request (PermissionID permission, Callback callback)
{
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission))
auto requestedPermission = jucePermissionToAndroidPermission (permission);
if (! isPermissionDeclaredInManifest (requestedPermission))
{
// Error! If you want to be able to request this runtime permission, you
// also need to declare it in your app's manifest. You can do so via
@@ -60,29 +212,50 @@ void RuntimePermissions::request (PermissionID permission, Callback callback)
return;
}
if (JUCE_ANDROID_API_VERSION < 23)
auto alreadyGranted = isGranted (permission);
if (alreadyGranted || getAndroidSDKVersion() < 23)
{
// There is no runtime permission system on API level below 23. As long as the
// permission is in the manifest (seems to be the case), we can simply ask Android
// if the app has the permission, and then directly call through to the callback.
callback (isGranted (permission));
callback (alreadyGranted);
return;
}
// we need to move the callback object to the heap so Java can keep track of the pointer
// and asynchronously pass it back to us (to be called and then deleted)
Callback* callbackPtr = new Callback (std::move (callback));
android.activity.callVoidMethod (JuceAppActivity.requestRuntimePermission, permission, (jlong) callbackPtr);
PermissionsRequest request (std::move (callback), permission);
static CriticalSection overlayGuard;
ScopedLock lock (overlayGuard);
std::unique_ptr<PermissionsOverlay>& overlay = PermissionsOverlay::getSingleton();
bool alreadyOpen = true;
if (overlay == nullptr)
{
overlay.reset (new PermissionsOverlay (overlayGuard));
alreadyOpen = false;
}
overlay->requests.push_back (std::move (request));
if (! alreadyOpen)
overlay->open();
}
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
{
return JUCE_ANDROID_API_VERSION >= 23;
return getAndroidSDKVersion() >= 23;
}
bool RuntimePermissions::isGranted (PermissionID permission)
{
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission);
auto* env = getEnv();
auto requestedPermission = jucePermissionToAndroidPermission (permission);
int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission,
javaString (requestedPermission).get());
return result == 0 /* PERMISSION_GRANTED */;
}
} // namespace juce

+ 17
- 344
modules/juce_core/native/juce_android_SystemStats.cpp View File

@@ -23,350 +23,23 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy")
#undef JNI_CLASS_MEMBERS
JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0)
{
getClasses().add (this);
}
JNIClassBase::~JNIClassBase()
{
getClasses().removeFirstMatchingValue (this);
}
Array<JNIClassBase*>& JNIClassBase::getClasses()
{
static Array<JNIClassBase*> classes;
return classes;
}
void JNIClassBase::initialise (JNIEnv* env)
{
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
jassert (classRef != 0);
initialiseFields (env);
}
void JNIClassBase::release (JNIEnv* env)
{
env->DeleteGlobalRef (classRef);
}
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->initialise (env);
}
void JNIClassBase::releaseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->release (env);
}
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
//==============================================================================
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass)
{
auto* env = getEnv();
implementer->javaSubClass = GlobalRef (subclass);
// you need to override at least one interface
jassert (interfaceNames.size() > 0);
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
LocalRef<jobject> classLoader;
for (auto i = 0; i < interfaceNames.size(); ++i)
{
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
if (aClass != nullptr)
{
if (i == 0)
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
}
else
{
// interface class not found
jassertfalse;
}
}
auto invocationHandler = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.createInvocationHandler,
reinterpret_cast<jlong> (implementer)));
// CreateJavaInterface() is expected to be called just once for a given implementer
jassert (implementer->invocationHandler == nullptr);
implementer->invocationHandler = GlobalRef (invocationHandler);
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
classLoader.get(), classArray.get(),
invocationHandler.get()));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames)
{
return CreateJavaInterface (implementer, interfaceNames,
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
JavaObject.constructor)));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName)
{
return CreateJavaInterface (implementer, StringArray (interfaceName));
}
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
{
if (invocationHandler != nullptr)
getEnv()->CallVoidMethod (android.activity,
JuceAppActivity.invocationHandlerContextDeleted,
invocationHandler.get());
}
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
{
auto* env = getEnv();
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
}
jobject juce_invokeImplementer (JNIEnv* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args)
{
setEnv (env);
return reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr)->invoke (proxy, method, args);
}
void juce_dispatchDelete (JNIEnv* env, jlong thisPtr)
{
setEnv (env);
delete reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchInvoke,
jobject, (JNIEnv* env, jobject /*object*/, jlong thisPtr, jobject proxy, jobject method, jobjectArray args))
{
return juce_invokeImplementer (env, thisPtr, proxy, method, args);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchFinalize,
void, (JNIEnv* env, jobject /*object*/, jlong thisPtr))
{
juce_dispatchDelete (env, thisPtr);
}
//==============================================================================
AppPausedResumedListener::AppPausedResumedListener (Owner& ownerToUse)
: owner (ownerToUse)
{
}
jobject AppPausedResumedListener::invoke (jobject proxy, jobject method, jobjectArray args)
{
auto* env = getEnv();
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
int numArgs = args != nullptr ? env->GetArrayLength (args) : 0;
if (methodName == "appPaused" && numArgs == 0)
{
owner.appPaused();
return nullptr;
}
if (methodName == "appResumed" && numArgs == 0)
{
owner.appResumed();
return nullptr;
}
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
class JniEnvThreadHolder
{
public:
static JniEnvThreadHolder& getInstance() noexcept
{
// You cann only use JNI functions AFTER JNI_OnLoad was called
jassert (androidJNIJavaVM != nullptr);
try
{
if (instance == nullptr)
instance = new JniEnvThreadHolder;
}
catch (...)
{
jassertfalse;
std::terminate();
}
return *instance;
}
static JNIEnv* getEnv() { return reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey)); }
static void setEnv (JNIEnv* env)
{
// env must not be a nullptr
jassert (env != nullptr);
#if JUCE_DEBUG
JNIEnv* oldenv = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
// This thread is already attached to the JavaVM and you trying to attach
// it to a different instance of the VM.
jassert (oldenv == nullptr || oldenv == env);
#endif
pthread_setspecific (getInstance().threadKey, env);
}
private:
pthread_key_t threadKey;
static void threadDetach (void* p)
{
if (JNIEnv* env = reinterpret_cast<JNIEnv*> (p))
{
ignoreUnused (env);
androidJNIJavaVM->DetachCurrentThread();
}
}
JniEnvThreadHolder()
{
pthread_key_create (&threadKey, threadDetach);
}
static JniEnvThreadHolder* instance;
};
JniEnvThreadHolder* JniEnvThreadHolder::instance = nullptr;
//==============================================================================
JNIEnv* attachAndroidJNI() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
if (env == nullptr)
{
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
}
return env;
}
JNIEnv* getEnv() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
// You are trying to use a JUCE function on a thread that was not created by JUCE.
// You need to first call setEnv on this thread before using JUCE
jassert (env != nullptr);
return env;
}
void setEnv (JNIEnv* env) noexcept { JniEnvThreadHolder::setEnv (env); }
extern "C" jint JNI_OnLoad (JavaVM* vm, void*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
androidJNIJavaVM = vm;
return JNI_VERSION_1_2;
}
//==============================================================================
AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160)
{
}
void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir)
{
setEnv (env);
screenWidth = screenHeight = 0;
dpi = 160;
JNIClassBase::initialiseAllClasses (env);
activity = GlobalRef (act);
appFile = juceString (env, file);
appDataDir = juceString (env, dataDir);
}
void AndroidSystem::shutdown (JNIEnv* env)
{
activity.clear();
JNIClassBase::releaseAllClasses (env);
}
AndroidSystem android;
//==============================================================================
namespace AndroidStatsHelpers
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")
DECLARE_JNI_CLASS (SystemClass, "java/lang/System")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getDefault, "getDefault", "()Ljava/util/Locale;") \
METHOD (getCountry, "getCountry", "()Ljava/lang/String;") \
METHOD (getLanguage, "getLanguage", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaLocale, "java/util/Locale")
#undef JNI_CLASS_MEMBERS
static inline String getSystemProperty (const String& name)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass,
@@ -376,19 +49,19 @@ namespace AndroidStatsHelpers
static inline String getLocaleValue (bool isRegion)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.getLocaleValue,
isRegion)));
}
auto* env = getEnv();
LocalRef<jobject> locale (env->CallStaticObjectMethod (JavaLocale, JavaLocale.getDefault));
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD)
DECLARE_JNI_CLASS (BuildClass, "android/os/Build")
#undef JNI_CLASS_MEMBERS
auto stringResult = isRegion ? env->CallObjectMethod (locale.get(), JavaLocale.getCountry)
: env->CallObjectMethod (locale.get(), JavaLocale.getLanguage);
return juceString (LocalRef<jstring> ((jstring) stringResult));
}
static inline String getAndroidOsBuildValue (const char* fieldName)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField (
BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;"))));
AndroidBuild, getEnv()->GetStaticFieldID (AndroidBuild, fieldName, "Ljava/lang/String;"))));
}
}


+ 318
- 0
modules/juce_core/native/juce_android_Threads.cpp View File

@@ -28,6 +28,322 @@ namespace juce
live in juce_posix_SharedCode.h!
*/
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (activityInfo, "activityInfo", "Landroid/content/pm/ActivityInfo;")
DECLARE_JNI_CLASS (AndroidResolveInfo, "android/content/pm/ResolveInfo")
#undef JNI_CLASS_MEMBERS
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
jobject androidApkContext = nullptr;
//==============================================================================
JNIEnv* getEnv() noexcept
{
if (androidJNIJavaVM != nullptr)
{
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
return env;
}
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
// before using any JUCE APIs. The Projucer will automatically generate java code
// which will invoke Thread::initialiseJUCE for you.
jassertfalse;
return nullptr;
}
void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context)
{
Thread::initialiseJUCE (env, context);
}
extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
androidJNIJavaVM = vm;
auto* env = getEnv();
// register the initialisation function
auto juceJavaClass = env->FindClass("com/roli/juce/Java");
if (juceJavaClass != nullptr)
{
JNINativeMethod method {"initialiseJUCE", "(Landroid/content/Context;)V",
reinterpret_cast<void*> (juce_JavainitialiseJUCE)};
auto status = env->RegisterNatives (juceJavaClass, &method, 1);
jassert (status == 0);
}
else
{
// com.roli.juce.Java class not found. Apparently this project is a library
// or was not generated by the Projucer. That's ok, the user will have to
// call Thread::initialiseJUCE manually
env->ExceptionClear();
}
JNIClassBase::initialiseAllClasses (env);
return JNI_VERSION_1_2;
}
//==============================================================================
class JuceActivityWatcher : public ActivityLifecycleCallbacks
{
public:
JuceActivityWatcher()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
checkActivityIsMain (androidApkContext);
}
~JuceActivityWatcher()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
clear();
myself.clear();
}
}
void onActivityStarted (jobject activity) override
{
auto* env = getEnv();
checkActivityIsMain (activity);
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
{
// see Clarification June 2001 in JNI reference for why this is
// necessary
LocalRef<jobject> localStorage (env->NewLocalRef (currentActivity));
if (env->IsSameObject (localStorage.get(), activity) != 0)
return;
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
}
if (activity != nullptr)
currentActivity = env->NewWeakGlobalRef (activity);
}
void onActivityStopped (jobject activity) override
{
auto* env = getEnv();
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
{
// important that the comparison happens in this order
// to avoid race condition where the weak reference becomes null
// just after the first check
if (env->IsSameObject (currentActivity, activity) != 0
|| env->IsSameObject (currentActivity, nullptr) != 0)
{
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
}
}
}
LocalRef<jobject> getCurrent()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (currentActivity));
}
LocalRef<jobject> getMain()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (mainActivity));
}
static JuceActivityWatcher& getInstance()
{
static JuceActivityWatcher activityWatcher;
return activityWatcher;
}
private:
void checkActivityIsMain (jobject context)
{
auto* env = getEnv();
ScopedLock lock (currentActivityLock);
if (mainActivity != nullptr)
{
if (env->IsSameObject (mainActivity, nullptr) != 0)
{
env->DeleteWeakGlobalRef (mainActivity);
mainActivity = nullptr;
}
}
if (mainActivity == nullptr)
{
LocalRef<jobject> appContext (getAppContext());
auto mainActivityPath = getMainActivityClassPath();
if (mainActivityPath.isNotEmpty())
{
auto clasz = env->GetObjectClass (context);
auto activityPath = juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (clasz, JavaClass.getName)));
// This may be problematic for apps which use several activities with the same type. We just
// assume that the very first activity of this type is the main one
if (activityPath == mainActivityPath)
mainActivity = env->NewWeakGlobalRef (context);
}
}
}
static String getMainActivityClassPath()
{
static String mainActivityClassPath;
if (mainActivityClassPath.isEmpty())
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> pkgManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
LocalRef<jstring> pkgName ((jstring) env->CallObjectMethod (appContext.get(), AndroidContext.getPackageName));
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
javaString ("android.intent.action.MAIN").get()));
intent = LocalRef<jobject> (env->CallObjectMethod (intent.get(),
AndroidIntent.setPackage,
pkgName.get()));
LocalRef<jobject> resolveInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.resolveActivity, intent.get(), 0));
if (resolveInfo != nullptr)
{
LocalRef<jobject> activityInfo (env->GetObjectField (resolveInfo.get(), AndroidResolveInfo.activityInfo));
LocalRef<jstring> jName ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.name));
LocalRef<jstring> jPackage ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.packageName));
mainActivityClassPath = juceString (jName);
}
}
}
return mainActivityClassPath;
}
GlobalRef myself;
CriticalSection currentActivityLock;
jweak currentActivity = nullptr;
jweak mainActivity = nullptr;
};
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
void juce_juceEventsAndroidStartApp();
#endif
void Thread::initialiseJUCE (void* jniEnv, void* context)
{
static CriticalSection cs;
ScopedLock lock (cs);
// jniEnv and context should not be null!
jassert (jniEnv != nullptr && context != nullptr);
auto* env = static_cast<JNIEnv*> (jniEnv);
if (androidJNIJavaVM == nullptr)
{
JavaVM* javaVM = nullptr;
auto status = env->GetJavaVM (&javaVM);
jassert (status == 0 && javaVM != nullptr);
androidJNIJavaVM = javaVM;
}
static bool firstCall = true;
if (firstCall)
{
firstCall = false;
// if we ever support unloading then this should probably be a weak reference
androidApkContext = env->NewGlobalRef (static_cast<jobject> (context));
JuceActivityWatcher::getInstance();
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
juce_juceEventsAndroidStartApp();
#endif
}
}
//==============================================================================
LocalRef<jobject> getAppContext() noexcept
{
auto* env = getEnv();
auto context = androidApkContext;
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
// before using any JUCE APIs. The Projucer will automatically generate java code
// which will invoke Thread::initialiseJUCE for you.
jassert (env != nullptr && context != nullptr);
if (context == nullptr)
return LocalRef<jobject>();
if (env->IsInstanceOf (context, AndroidApplication) != 0)
return LocalRef<jobject> (env->NewLocalRef (context));
LocalRef<jobject> applicationContext (env->CallObjectMethod (context, AndroidContext.getApplicationContext));
if (applicationContext == nullptr)
return LocalRef<jobject> (env->NewLocalRef (context));
return applicationContext;
}
LocalRef<jobject> getCurrentActivity() noexcept
{
return JuceActivityWatcher::getInstance().getCurrent();
}
LocalRef<jobject> getMainActivity() noexcept
{
return JuceActivityWatcher::getInstance().getMain();
}
//==============================================================================
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
@@ -74,4 +390,6 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
} // namespace juce

+ 16
- 28
modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -640,9 +640,6 @@ MemoryMappedFile::~MemoryMappedFile()
File juce_getExecutableFile();
File juce_getExecutableFile()
{
#if JUCE_ANDROID
return File (android.appFile);
#else
struct DLAddrReader
{
static String getFilename()
@@ -657,7 +654,6 @@ File juce_getExecutableFile()
static String filename = DLAddrReader::getFilename();
return File::getCurrentWorkingDirectory().getChildFile (filename);
#endif
}
//==============================================================================
@@ -891,28 +887,27 @@ void JUCE_API juce_threadEntryPoint (void*);
extern JavaVM* androidJNIJavaVM;
#endif
static void* threadEntryProc (void* userData)
extern "C" void* threadEntryProc (void*);
extern "C" void* threadEntryProc (void* userData)
{
#if JUCE_ANDROID
auto* myself = static_cast<Thread*> (userData);
if (androidJNIJavaVM != nullptr)
{
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
}
else
JUCE_AUTORELEASEPOOL
{
// JNI_OnLoad was not called - make sure you load the JUCE shared library
// using System.load inside of Java
jassertfalse;
juce_threadEntryPoint (myself);
}
#endif
JUCE_AUTORELEASEPOOL
#if JUCE_ANDROID
if (androidJNIJavaVM != nullptr)
{
juce_threadEntryPoint (userData);
void* env = nullptr;
androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
// only detach if we have actually been attached
if (env != nullptr)
androidJNIJavaVM->DetachCurrentThread();
}
#endif
return nullptr;
}
@@ -955,6 +950,7 @@ void Thread::launchThread()
pthread_attr_setstacksize (attrPtr, threadStackSize);
}
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
{
pthread_detach (handle);
@@ -1335,15 +1331,7 @@ private:
static void* timerThread (void* param)
{
#if JUCE_ANDROID
// JNI_OnLoad was not called - make sure you load the JUCE shared library
// using System.load inside of Java
jassert (androidJNIJavaVM != nullptr);
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
#else
#if ! JUCE_ANDROID
int dummy;
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy);
#endif


+ 41
- 0
modules/juce_core/threads/juce_Thread.h View File

@@ -324,6 +324,47 @@ public:
*/
static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName);
#if JUCE_ANDROID || defined (DOXYGEN)
//==============================================================================
/** Initialises the JUCE subsystem for projects not created by the Projucer
On Android, JUCE needs to be initialised once before it is used. The Projucer
will automatically generate the necessary java code to do this. However, if
you are using JUCE without the Projucer or are creating a library made with
JUCE intended for use in non-JUCE apks, then you must call this method
manually once on apk startup.
You can call this method from C++ or directly from java by calling the
following java method:
@code
com.roli.juce.Java.initialiseJUCE (myContext);
@endcode
Note that the above java method is only available in Android Studio projects
created by the Projucer. If you need to call this from another type of project
then you need to add the following java file to
your project:
@code
package com.roli.juce;
public class Java
{
static { System.loadLibrary ("juce_jni"); }
public native static void initialiseJUCE (Context context);
}
@endcode
@param jniEnv this is a pointer to JNI's JNIEnv variable. Any callback
from Java into C++ will have this passed in as it's first
parameter.
@param jContext this is a jobject referring to your app/service/receiver/
provider's Context. JUCE needs this for many of it's internal
functions.
*/
static void initialiseJUCE (void* jniEnv, void* jContext);
#endif
private:
//==============================================================================


+ 1
- 1
modules/juce_events/messages/juce_Initialisation.h View File

@@ -116,7 +116,7 @@ public:
#elif JUCE_ANDROID
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
extern "C" juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
#define JUCE_MAIN_FUNCTION_DEFINITION


+ 149
- 10
modules/juce_events/native/juce_android_Messaging.cpp View File

@@ -50,7 +50,7 @@ namespace Android
struct Handler
{
Handler() : nativeHandler (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor)) {}
Handler() : nativeHandler (LocalRef<jobject> (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor))) {}
~Handler() { clearSingletonInstance(); }
JUCE_DECLARE_SINGLETON (Handler, false)
@@ -72,7 +72,7 @@ struct AndroidMessageQueue : private Android::Runnable
JUCE_DECLARE_SINGLETON_SINGLETHREADED (AndroidMessageQueue, true)
AndroidMessageQueue()
: self (CreateJavaInterface (this, "java/lang/Runnable").get())
: self (CreateJavaInterface (this, "java/lang/Runnable"))
{
}
@@ -131,6 +131,7 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons
{
return AndroidMessageQueue::getInstance()->post (message);
}
//==============================================================================
void MessageManager::broadcastMessage (const String&)
{
@@ -149,18 +150,26 @@ void MessageManager::stopDispatchLoop()
void messageCallback() override
{
auto* env = getEnv();
LocalRef<jobject> activity (getCurrentActivity());
if (activity != nullptr)
{
jmethodID quitMethod = env->GetMethodID (AndroidActivity, "finishAndRemoveTask", "()V");
jmethodID quitMethod = env->GetMethodID (JuceAppActivity, "finishAndRemoveTask", "()V");
if (quitMethod != 0)
{
env->CallVoidMethod (activity.get(), quitMethod);
return;
}
if (quitMethod != 0)
quitMethod = env->GetMethodID (AndroidActivity, "finish", "()V");
jassert (quitMethod != 0);
env->CallVoidMethod (activity.get(), quitMethod);
}
else
{
env->CallVoidMethod (android.activity, quitMethod);
return;
jassertfalse;
}
quitMethod = env->GetMethodID (JuceAppActivity, "finish", "()V");
jassert (quitMethod != 0);
env->CallVoidMethod (android.activity, quitMethod);
}
};
@@ -168,4 +177,134 @@ void MessageManager::stopDispatchLoop()
quitMessagePosted = true;
}
//==============================================================================
class JuceAppLifecycle : public ActivityLifecycleCallbacks
{
public:
JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)())
: createApplicationSymbol (initSymbolAddr)
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
}
~JuceAppLifecycle()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
clear();
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
myself.clear();
}
}
void onActivityCreated (jobject, jobject) override
{
checkCreated();
}
void onActivityDestroyed (jobject activity) override
{
auto* env = getEnv();
// if the main activity is being destroyed, only then tear-down JUCE
if (env->IsSameObject (getMainActivity().get(), activity) != 0)
{
JUCEApplicationBase::appWillTerminateByForce();
JNIClassBase::releaseAllClasses (env);
jclass systemClass = (jclass) env->FindClass ("java/lang/System");
jmethodID exitMethod = env->GetStaticMethodID (systemClass, "exit", "(I)V");
env->CallStaticVoidMethod (systemClass, exitMethod, 0);
}
}
void onActivityStarted (jobject) override
{
checkCreated();
}
void onActivityPaused (jobject) override
{
if (auto* app = JUCEApplicationBase::getInstance())
app->suspended();
}
void onActivityResumed (jobject) override
{
checkInitialised();
if (auto* app = JUCEApplicationBase::getInstance())
app->resumed();
}
static JuceAppLifecycle& getInstance (juce::JUCEApplicationBase* (*initSymbolAddr)())
{
static JuceAppLifecycle juceAppLifecycle (initSymbolAddr);
return juceAppLifecycle;
}
private:
void checkCreated()
{
if (JUCEApplicationBase::getInstance() == nullptr)
{
DBG (SystemStats::getJUCEVersion());
JUCEApplicationBase::createInstance = createApplicationSymbol;
initialiseJuce_GUI();
if (! JUCEApplicationBase::createInstance())
jassertfalse; // you must supply an application object for an android app!
jassert (MessageManager::getInstance()->isThisTheMessageThread());
}
}
void checkInitialised()
{
checkCreated();
if (! hasBeenInitialised)
{
if (auto* app = JUCEApplicationBase::getInstance())
{
hasBeenInitialised = app->initialiseApp();
if (! hasBeenInitialised)
exit (app->shutdownApp());
}
}
}
GlobalRef myself;
juce::JUCEApplicationBase* (*createApplicationSymbol)();
bool hasBeenInitialised = false;
};
//==============================================================================
File juce_getExecutableFile();
void juce_juceEventsAndroidStartApp()
{
auto dllPath = juce_getExecutableFile().getFullPathName();
auto addr = reinterpret_cast<juce::JUCEApplicationBase*(*)()> (DynamicLibrary (dllPath)
.getFunction ("juce_CreateApplication"));
if (addr != nullptr)
JuceAppLifecycle::getInstance (addr);
}
} // namespace juce

+ 178
- 30
modules/juce_graphics/native/juce_android_Fonts.cpp View File

@@ -94,11 +94,37 @@ bool TextLayout::createNativeLayout (const AttributedString&)
#else
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
STATICMETHOD (createFromAsset, "createFromAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;)Landroid/graphics/Typeface;")
DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface")
DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (computeBounds, "computeBounds", "(Landroid/graphics/RectF;Z)V")
DECLARE_JNI_CLASS (AndroidPath, "android/graphics/Path")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
FIELD (left, "left", "F") \
FIELD (right, "right", "F") \
FIELD (top, "top", "F") \
FIELD (bottom, "bottom", "F") \
METHOD (roundOut, "roundOut", "(Landroid/graphics/Rect;)V")
DECLARE_JNI_CLASS (AndroidRectF, "android/graphics/RectF")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getInstance, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;") \
METHOD (update, "update", "([B)V") \
METHOD (digest, "digest", "()[B")
DECLARE_JNI_CLASS (JavaMessageDigest, "java/security/MessageDigest")
#undef JNI_CLASS_MEMBERS
//==============================================================================
@@ -136,8 +162,7 @@ public:
JNIEnv* const env = getEnv();
// First check whether there's an embedded asset with this font name:
typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset,
javaString ("fonts/" + name).get()));
typeface = GlobalRef (getTypefaceFromAsset (name));
if (typeface.get() == nullptr)
{
@@ -150,12 +175,12 @@ public:
fontFile = findFontFile (name, isBold, isItalic);
if (fontFile.exists())
typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
javaString (fontFile.getFullPathName()).get()));
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
javaString (fontFile.getFullPathName()).get())));
else
typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
javaString (getName()).get(),
(isBold ? 1 : 0) + (isItalic ? 2 : 0)));
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
javaString (getName()).get(),
(isBold ? 1 : 0) + (isItalic ? 2 : 0))));
}
initialise (env);
@@ -164,23 +189,24 @@ public:
AndroidTypeface (const void* data, size_t size)
: Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
{
JNIEnv* const env = getEnv();
auto* env = getEnv();
auto cacheFile = getCacheFileForData (data, size);
LocalRef<jbyteArray> bytes (env->NewByteArray ((jsize) size));
env->SetByteArrayRegion (bytes, 0, (jsize) size, (const jbyte*) data);
typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromByteArray, bytes.get()));
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
javaString (cacheFile.getFullPathName()).get())));
initialise (env);
}
void initialise (JNIEnv* const env)
{
rect = GlobalRef (env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0));
rect = GlobalRef (LocalRef<jobject>(env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0)));
paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality));
const LocalRef<jobject> ignored (paint.callObjectMethod (AndroidPaint.setTypeface, typeface.get()));
charArray = GlobalRef (LocalRef<jobject>((jobject) env->NewCharArray (2)));
paint.callVoidMethod (AndroidPaint.setTextSize, referenceFontSize);
const float fullAscent = std::abs (paint.callFloatMethod (AndroidPaint.ascent));
@@ -291,20 +317,56 @@ public:
#else
jchar ch1 = glyphNumber, ch2 = 0;
#endif
Rectangle<int> bounds;
auto* env = getEnv();
{
LocalRef<jobject> matrix (GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t)));
jboolean isCopy;
auto* buffer = env->GetCharArrayElements ((jcharArray) charArray.get(), &isCopy);
buffer[0] = ch1; buffer[1] = ch2;
env->ReleaseCharArrayElements ((jcharArray) charArray.get(), buffer, 0);
LocalRef<jobject> path (env->NewObject (AndroidPath, AndroidPath.constructor));
LocalRef<jobject> boundsF (env->NewObject (AndroidRectF, AndroidRectF.constructor));
JNIEnv* env = getEnv();
jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t));
jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, ch1, ch2, paint.get(), matrix, rect.get());
env->CallVoidMethod (paint.get(), AndroidPaint.getCharsPath, charArray.get(), 0, (ch2 != 0 ? 2 : 1), 0.0f, 0.0f, path.get());
env->DeleteLocalRef (matrix);
env->CallVoidMethod (path.get(), AndroidPath.computeBounds, boundsF.get(), 1);
const int left = env->GetIntField (rect.get(), AndroidRect.left);
const int top = env->GetIntField (rect.get(), AndroidRect.top);
const int right = env->GetIntField (rect.get(), AndroidRect.right);
const int bottom = env->GetIntField (rect.get(), AndroidRect.bottom);
env->CallBooleanMethod (matrix.get(), AndroidMatrix.mapRect, boundsF.get());
const Rectangle<int> bounds (left, top, right - left, bottom - top);
env->CallVoidMethod (boundsF.get(), AndroidRectF.roundOut, rect.get());
bounds = Rectangle<int>::leftTopRightBottom (env->GetIntField (rect.get(), AndroidRect.left) - 1,
env->GetIntField (rect.get(), AndroidRect.top),
env->GetIntField (rect.get(), AndroidRect.right) + 1,
env->GetIntField (rect.get(), AndroidRect.bottom));
auto w = bounds.getWidth();
auto h = jmax (1, bounds.getHeight());
LocalRef<jobject> bitmapConfig (env->CallStaticObjectMethod (AndroidBitmapConfig, AndroidBitmapConfig.valueOf, javaString ("ARGB_8888").get()));
LocalRef<jobject> bitmap (env->CallStaticObjectMethod (AndroidBitmap, AndroidBitmap.createBitmap, w, h, bitmapConfig.get()));
LocalRef<jobject> canvas (env->NewObject (AndroidCanvas, AndroidCanvas.create, bitmap.get()));
env->CallBooleanMethod (matrix.get(), AndroidMatrix.postTranslate, bounds.getX() * -1.0f, bounds.getY() * -1.0f);
env->CallVoidMethod (canvas.get(), AndroidCanvas.setMatrix, matrix.get());
env->CallVoidMethod (canvas.get(), AndroidCanvas.drawPath, path.get(), paint.get());
int requiredRenderArraySize = w * h;
if (requiredRenderArraySize > lastCachedRenderArraySize)
{
cachedRenderArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewIntArray (requiredRenderArraySize)));
lastCachedRenderArraySize = requiredRenderArraySize;
}
env->CallVoidMethod (bitmap.get(), AndroidBitmap.getPixels, cachedRenderArray.get(), 0, w, 0, 0, w, h);
env->CallVoidMethod (bitmap.get(), AndroidBitmap.recycle);
}
EdgeTable* et = nullptr;
@@ -312,10 +374,10 @@ public:
{
et = new EdgeTable (bounds);
jint* const maskDataElements = env->GetIntArrayElements (maskData, 0);
jint* const maskDataElements = env->GetIntArrayElements ((jintArray) cachedRenderArray.get(), 0);
const jint* mask = maskDataElements;
for (int y = top; y < bottom; ++y)
for (int y = bounds.getY(); y < bounds.getBottom(); ++y)
{
#if JUCE_LITTLE_ENDIAN
const uint8* const lineBytes = ((const uint8*) mask) + 3;
@@ -323,19 +385,19 @@ public:
const uint8* const lineBytes = (const uint8*) mask;
#endif
et->clipLineToMask (left, y, lineBytes, 4, bounds.getWidth());
et->clipLineToMask (bounds.getX(), y, lineBytes, 4, bounds.getWidth());
mask += bounds.getWidth();
}
env->ReleaseIntArrayElements (maskData, maskDataElements, 0);
env->ReleaseIntArrayElements ((jintArray) cachedRenderArray.get(), maskDataElements, 0);
}
env->DeleteLocalRef (maskData);
return et;
}
GlobalRef typeface, paint, rect;
GlobalRef typeface, paint, rect, charArray, cachedRenderArray;
float ascent, descent, heightToPointsFactor;
int lastCachedRenderArraySize = -1;
private:
static File findFontFile (const String& family,
@@ -373,6 +435,92 @@ private:
return File (path + ".ttf");
}
static LocalRef<jobject> getTypefaceFromAsset (const String& typefaceName)
{
auto* env = getEnv();
LocalRef<jobject> assetManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getAssets));
if (assetManager == nullptr)
return LocalRef<jobject>();
auto assetTypeface = env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromAsset, assetManager.get(),
javaString ("fonts/" + typefaceName).get());
// this may throw
if (env->ExceptionCheck() != 0)
{
env->ExceptionClear();
return LocalRef<jobject>();
}
return LocalRef<jobject> (assetTypeface);
}
static File getCacheDirectory()
{
static File result = [] ()
{
auto appContext = getAppContext();
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> cacheFile (env->CallObjectMethod (appContext.get(), AndroidContext.getCacheDir));
LocalRef<jstring> jPath ((jstring) env->CallObjectMethod (cacheFile.get(), JavaFile.getAbsolutePath));
return File (juceString (env, jPath.get()));
}
jassertfalse;
return File();
} ();
return result;
}
static HashMap<String, File>& getInMemoryFontCache()
{
static HashMap<String, File> cache;
return cache;
}
static File getCacheFileForData (const void* data, size_t size)
{
static CriticalSection cs;
JNIEnv* const env = getEnv();
String key;
{
LocalRef<jobject> digest (env->CallStaticObjectMethod (JavaMessageDigest, JavaMessageDigest.getInstance, javaString("MD5").get()));
LocalRef<jbyteArray> bytes(env->NewByteArray(size));
jboolean ignore;
auto* jbytes = env->GetByteArrayElements(bytes.get(), &ignore);
memcpy(jbytes, data, size);
env->ReleaseByteArrayElements(bytes.get(), jbytes, 0);
env->CallVoidMethod(digest.get(), JavaMessageDigest.update, bytes.get());
LocalRef<jbyteArray> result((jbyteArray) env->CallObjectMethod(digest.get(), JavaMessageDigest.digest));
auto* md5Bytes = env->GetByteArrayElements(result.get(), &ignore);
key = String::toHexString(md5Bytes, env->GetArrayLength(result.get()), 0);
env->ReleaseByteArrayElements(result.get(), md5Bytes, 0);
}
ScopedLock lock (cs);
auto& mapEntry = getInMemoryFontCache().getReference (key);
if (mapEntry == File())
{
mapEntry = getCacheDirectory().getChildFile ("bindata_" + key);
mapEntry.replaceWithData (data, size);
}
return mapEntry;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface)
};


+ 4
- 5
modules/juce_graphics/native/juce_android_GraphicsContext.cpp View File

@@ -29,7 +29,7 @@ namespace juce
namespace GraphicsHelpers
{
jobject createPaint (Graphics::ResamplingQuality quality)
LocalRef<jobject> createPaint (Graphics::ResamplingQuality quality)
{
jint constructorFlags = 1 /*ANTI_ALIAS_FLAG*/
| 4 /*DITHER_FLAG*/
@@ -38,13 +38,12 @@ namespace GraphicsHelpers
if (quality > Graphics::lowResamplingQuality)
constructorFlags |= 2; /*FILTER_BITMAP_FLAG*/
return getEnv()->NewObject (AndroidPaint, AndroidPaint.constructor, constructorFlags);
return LocalRef<jobject>(getEnv()->NewObject (AndroidPaint, AndroidPaint.constructor, constructorFlags));
}
//
const jobject createMatrix (JNIEnv* env, const AffineTransform& t)
const LocalRef<jobject> createMatrix (JNIEnv* env, const AffineTransform& t)
{
jobject m = env->NewObject (AndroidMatrix, AndroidMatrix.constructor);
auto m = LocalRef<jobject>(env->NewObject (AndroidMatrix, AndroidMatrix.constructor));
jfloat values[9] = { t.mat00, t.mat01, t.mat02,
t.mat10, t.mat11, t.mat12,


+ 0
- 7
modules/juce_gui_basics/filebrowser/juce_ContentSharer.h View File

@@ -139,13 +139,6 @@ private:
void deleteTemporaryFiles();
void sharingFinished (bool, const String&);
#if JUCE_ANDROID
friend void* juce_contentSharerOpenFile (void*, void*, void*);
friend void* juce_contentSharerQuery (void*, void*, void*, void*, void*, void*);
friend void* juce_contentSharerGetStreamTypes (void*, void*);
friend void juce_contentSharingCompleted (int);
#endif
};
} // namespace juce

+ 1
- 1
modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -598,7 +598,7 @@ struct MenuWindow : public Component
targetPoint = relativeTo->localPointToGlobal (targetPoint);
auto parentArea = Desktop::getInstance().getDisplays().findDisplayForPoint (targetPoint)
#if JUCE_MAC
#if JUCE_MAC || JUCE_ANDROID
.userArea;
#else
.totalArea; // on windows, don't stop the menu overlapping the taskbar


+ 493
- 0
modules/juce_gui_basics/native/java/com/roli/juce/ComponentPeerView.java View File

@@ -0,0 +1,493 @@
package com.roli.juce;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.text.InputType;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;

import java.lang.reflect.Method;

public final class ComponentPeerView extends ViewGroup
implements View.OnFocusChangeListener, Application.ActivityLifecycleCallbacks
{
public ComponentPeerView (Context context, boolean opaque_, long host)
{
super (context);

if (Application.class.isInstance (context))
{
((Application) context).registerActivityLifecycleCallbacks (this);
} else
{
((Application) context.getApplicationContext ()).registerActivityLifecycleCallbacks (this);
}

this.host = host;
setWillNotDraw (false);
opaque = opaque_;

setFocusable (true);
setFocusableInTouchMode (true);
setOnFocusChangeListener (this);

// swap red and blue colours to match internal opengl texture format
ColorMatrix colorMatrix = new ColorMatrix ();

float[] colorTransform = {0, 0, 1.0f, 0, 0,
0, 1.0f, 0, 0, 0,
1.0f, 0, 0, 0, 0,
0, 0, 0, 1.0f, 0};

colorMatrix.set (colorTransform);
paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));

java.lang.reflect.Method method = null;

try
{
method = getClass ().getMethod ("setLayerType", int.class, Paint.class);
} catch (SecurityException e)
{
} catch (NoSuchMethodException e)
{
}

if (method != null)
{
try
{
int layerTypeNone = 0;
method.invoke (this, layerTypeNone, null);
} catch (java.lang.IllegalArgumentException e)
{
} catch (java.lang.IllegalAccessException e)
{
} catch (java.lang.reflect.InvocationTargetException e)
{
}
}
}

public void clear ()
{
host = 0;
}

//==============================================================================
private native void handlePaint (long host, Canvas canvas, Paint paint);

@Override
public void onDraw (Canvas canvas)
{
if (host == 0)
return;

handlePaint (host, canvas, paint);
}

@Override
public boolean isOpaque ()
{
return opaque;
}

private boolean opaque;
private long host;
private Paint paint = new Paint ();

//==============================================================================
private native void handleMouseDown (long host, int index, float x, float y, long time);
private native void handleMouseDrag (long host, int index, float x, float y, long time);
private native void handleMouseUp (long host, int index, float x, float y, long time);

@Override
public boolean onTouchEvent (MotionEvent event)
{
if (host == 0)
return false;

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);
return true;

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
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);

int n = event.getPointerCount ();

if (n > 1)
{
int point[] = new int[2];
getLocationOnScreen (point);

for (int i = 1; i < n; ++i)
handleMouseDrag (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
}

return true;
}

case MotionEvent.ACTION_POINTER_UP:
{
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

if (i == 0)
{
handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
} else
{
int point[] = new int[2];
getLocationOnScreen (point);

handleMouseUp (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
}
return true;
}

case MotionEvent.ACTION_POINTER_DOWN:
{
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

if (i == 0)
{
handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
} else
{
int point[] = new int[2];
getLocationOnScreen (point);

handleMouseDown (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
}
return true;
}

default:
break;
}

return false;
}

//==============================================================================
private native void handleKeyDown (long host, int keycode, int textchar);
private native void handleKeyUp (long host, int keycode, int textchar);
private native void handleBackButton (long host);
private native void handleKeyboardHidden (long host);

public void showKeyboard (String type)
{
InputMethodManager imm = (InputMethodManager) getContext ().getSystemService (Context.INPUT_METHOD_SERVICE);

if (imm != null)
{
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 ();
}
}
}

public void backButtonPressed ()
{
if (host == 0)
return;

handleBackButton (host);
}

@Override
public boolean onKeyDown (int keyCode, KeyEvent event)
{
if (host == 0)
return false;

switch (keyCode)
{
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
return super.onKeyDown (keyCode, event);
case KeyEvent.KEYCODE_BACK:
{
((Activity) getContext ()).onBackPressed ();
return true;
}

default:
break;
}

handleKeyDown (host, keyCode, event.getUnicodeChar ());
return true;
}

@Override
public boolean onKeyUp (int keyCode, KeyEvent event)
{
if (host == 0)
return false;

handleKeyUp (host, keyCode, event.getUnicodeChar ());
return true;
}

@Override
public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
{
if (host == 0)
return false;

if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction () != KeyEvent.ACTION_MULTIPLE)
return super.onKeyMultiple (keyCode, count, event);

if (event.getCharacters () != null)
{
int utf8Char = event.getCharacters ().codePointAt (0);
handleKeyDown (host, utf8Char, utf8Char);
return true;
}

return false;
}

//==============================================================================
private final class KeyboardDismissListener
{
public KeyboardDismissListener (ComponentPeerView viewToUse)
{
view = viewToUse;
}

private void startListening ()
{
view.getViewTreeObserver ().addOnGlobalLayoutListener (viewTreeObserver);
}

private void stopListening ()
{
view.getViewTreeObserver ().removeGlobalOnLayoutListener (viewTreeObserver);
}

private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
{
TreeObserver ()
{
keyboardShown = false;
}

@Override
public void onGlobalLayout ()
{
Rect r = new Rect ();

View parentView = getRootView ();
int diff = 0;

if (parentView == null)
{
getWindowVisibleDisplayFrame (r);
diff = getHeight () - (r.bottom - r.top);
} else
{
parentView.getWindowVisibleDisplayFrame (r);
diff = parentView.getHeight () - (r.bottom - r.top);
}

// Arbitrary threshold, surely keyboard would take more than 20 pix.
if (diff < 20 && keyboardShown)
{
keyboardShown = false;
handleKeyboardHidden (view.host);
}

if (!keyboardShown && diff > 20)
keyboardShown = true;
}

;

private boolean keyboardShown;
}

;

private ComponentPeerView view;
private TreeObserver viewTreeObserver = new TreeObserver ();
}

private KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this);

// this is here to make keyboard entry work on a Galaxy Tab2 10.1
@Override
public InputConnection onCreateInputConnection (EditorInfo outAttrs)
{
outAttrs.actionLabel = "";
outAttrs.hintText = "";
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -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);
}

//==============================================================================
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh)
{
super.onSizeChanged (w, h, oldw, oldh);

if (host != 0)
viewSizeChanged (host);
}

@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
{
}

private native void viewSizeChanged (long host);

@Override
public void onFocusChange (View v, boolean hasFocus)
{
if (host == 0)
return;

if (v == this)
focusChanged (host, hasFocus);
}

private native void focusChanged (long host, boolean hasFocus);

public void setViewName (String newName)
{
}

public void setSystemUiVisibilityCompat (int visibility)
{
Method systemUIVisibilityMethod = null;
try
{
systemUIVisibilityMethod = this.getClass ().getMethod ("setSystemUiVisibility", int.class);
} catch (SecurityException e)
{
return;
} catch (NoSuchMethodException e)
{
return;
}
if (systemUIVisibilityMethod == null) return;

try
{
systemUIVisibilityMethod.invoke (this, visibility);
} catch (java.lang.IllegalArgumentException e)
{
} catch (java.lang.IllegalAccessException e)
{
} catch (java.lang.reflect.InvocationTargetException e)
{
}
}

public boolean isVisible ()
{
return getVisibility () == VISIBLE;
}

public void setVisible (boolean b)
{
setVisibility (b ? VISIBLE : INVISIBLE);
}

public boolean containsPoint (int x, int y)
{
return true; //xxx needs to check overlapping views
}

//==============================================================================
private native void handleAppPaused (long host);
private native void handleAppResumed (long host);

@Override
public void onActivityPaused (Activity activity)
{
if (host == 0)
return;

handleAppPaused (host);
}

@Override
public void onActivityStopped (Activity activity)
{

}

@Override
public void onActivitySaveInstanceState (Activity activity, Bundle bundle)
{

}

@Override
public void onActivityDestroyed (Activity activity)
{

}

@Override
public void onActivityCreated (Activity activity, Bundle bundle)
{

}

@Override
public void onActivityStarted (Activity activity)
{

}

@Override
public void onActivityResumed (Activity activity)
{
if (host == 0)
return;

// Ensure that navigation/status bar visibility is correctly restored.
handleAppResumed (host);
}
}

modules/juce_core/native/java/AndroidSharingContentProvider.java → modules/juce_gui_basics/native/javacore/app/com/roli/juce/JuceSharingContentProvider.java View File

@@ -1,4 +1,4 @@
package com.juce;
package com.roli.juce;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -9,19 +9,15 @@ import android.database.MatrixCursor;
import android.net.Uri;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import java.lang.String;
public final class SharingContentProvider extends ContentProvider
public final class JuceSharingContentProvider extends ContentProvider
{
private Object lock = new Object();
private native void contentSharerFileObserverEvent (long host, int event, String path);
private Object lock = new Object ();
private native Cursor contentSharerQuery (Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder);
private native void contentSharerCursorClosed (long host);
private native AssetFileDescriptor contentSharerOpenFile (Uri uri, String mode);
private native String[] contentSharerGetStreamTypes (Uri uri, String mimeTypeFilter);
@@ -40,6 +36,8 @@ public final class SharingContentProvider extends ContentProvider
}
private long host;
private native void contentSharerFileObserverEvent (long host, int event, String path);
}
public final class ProviderCursor extends MatrixCursor
@@ -52,18 +50,20 @@ public final class SharingContentProvider extends ContentProvider
}
@Override
public void close()
public void close ()
{
super.close();
super.close ();
contentSharerCursorClosed (host);
}
private native void contentSharerCursorClosed (long host);
private long host;
}
@Override
public boolean onCreate()
public boolean onCreate ()
{
return true;
}
@@ -120,13 +120,12 @@ public final class SharingContentProvider extends ContentProvider
AssetFileDescriptor result = contentSharerOpenFile (uri, mode);
if (result != null)
return result.getParcelFileDescriptor();
return result.getParcelFileDescriptor ();
return null;
}
}
$$ContentProviderApi11
@Override
public String[] getStreamTypes (Uri uri, String mimeTypeFilter)
{
synchronized (lock)
@@ -134,5 +133,4 @@ $$ContentProviderApi11
return contentSharerGetStreamTypes (uri, mimeTypeFilter);
}
}
ContentProviderApi11$$
}

+ 174
- 115
modules/juce_gui_basics/native/juce_android_ContentSharer.cpp View File

@@ -23,23 +23,76 @@
==============================================================================
*/
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
FIELD (providers, "providers", "[Landroid/content/pm/ProviderInfo;")
DECLARE_JNI_CLASS (AndroidPackageInfo, "android/content/pm/PackageInfo")
#undef JNI_CLASS_MEMBERS
//==============================================================================
// This byte-code is generated from native/javacore/app/com/roli/juce/JuceSharingContentProvider.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaJuceSharingContentProvider[] =
{31,139,8,8,143,106,229,91,0,3,74,117,99,101,83,104,97,114,105,110,103,67,111,110,116,101,110,116,80,114,111,118,105,100,
101,114,46,100,101,120,0,149,151,93,108,20,85,20,199,207,157,153,157,253,236,178,91,170,20,145,178,229,83,80,216,242,165,
96,5,11,45,72,183,91,139,161,52,218,190,56,221,157,148,129,221,153,101,102,118,133,23,2,106,162,209,196,24,125,64,19,73,
48,33,106,140,15,36,26,227,131,49,152,24,163,241,65,77,148,248,160,209,152,152,24,193,68,227,131,6,37,241,127,63,118,219,
173,197,232,194,111,238,185,231,156,123,238,185,231,222,153,206,148,237,19,137,190,173,219,105,239,208,216,231,67,47,106,
177,200,154,39,135,207,172,189,226,63,113,230,173,189,99,175,63,244,123,185,131,168,70,68,39,38,182,117,146,250,157,79,
17,141,146,212,223,4,46,48,34,110,252,3,109,4,237,103,26,209,82,222,71,171,163,189,132,203,80,156,40,103,16,125,111,18,
253,4,126,6,191,129,107,224,58,232,137,18,245,130,53,96,3,216,2,14,131,6,120,25,188,11,190,1,191,128,100,140,104,19,112,
192,235,224,50,184,6,110,193,28,187,192,3,192,6,117,240,52,120,6,60,15,206,130,115,224,101,240,10,120,3,188,9,222,6,159,
128,175,192,183,224,42,136,38,136,214,129,33,48,5,60,240,8,56,5,206,130,87,193,69,240,54,120,31,124,12,62,5,95,130,31,
192,21,240,43,248,19,24,73,162,197,96,57,88,5,242,224,78,176,27,12,131,7,65,9,56,224,56,56,9,78,129,199,192,83,0,101,37,
148,142,16,138,208,37,148,159,176,45,148,6,139,64,6,100,73,238,193,98,208,165,246,229,102,176,4,116,147,220,143,91,193,
106,176,134,228,190,240,223,195,168,189,166,228,10,228,152,154,235,4,100,148,65,236,231,105,165,71,233,233,89,200,248,47,
108,252,23,83,50,247,143,170,60,94,48,229,92,205,3,179,92,201,231,249,62,43,249,53,200,43,148,124,17,242,42,37,191,11,
121,165,146,63,130,220,171,228,47,32,231,148,252,181,41,215,177,120,78,14,93,42,135,4,170,181,85,212,42,69,247,137,122,
201,126,82,245,83,168,214,157,196,215,28,19,99,13,172,176,143,248,154,22,137,190,9,253,58,17,51,45,250,9,81,105,222,74,
125,2,255,214,171,120,36,218,36,109,16,109,156,238,17,241,101,220,20,42,113,187,104,53,186,67,180,58,109,20,45,163,77,
202,190,89,180,81,218,34,90,131,182,171,252,250,213,184,93,162,53,105,183,26,191,71,237,253,1,177,231,49,149,151,172,185,
169,106,193,247,171,15,157,109,50,61,113,94,178,170,70,77,251,0,236,35,202,158,82,118,109,142,253,32,236,211,202,206,245,
157,144,187,83,179,114,111,74,158,201,13,41,238,31,17,250,231,146,114,142,41,198,168,150,211,104,128,38,53,126,66,117,
120,242,179,118,46,41,207,137,151,209,225,127,8,91,89,235,139,146,198,210,34,119,83,248,92,104,197,208,97,53,104,32,50,
169,105,136,17,129,149,231,117,49,41,215,121,8,241,107,227,113,210,54,167,17,139,137,92,222,73,202,181,214,50,60,183,149,
168,79,45,195,207,253,84,198,16,59,25,17,167,154,232,189,164,90,7,246,155,199,229,249,125,152,148,117,24,239,53,104,57,
171,245,165,104,139,145,162,30,150,197,222,247,176,117,34,183,152,152,39,78,186,170,212,103,173,56,89,68,150,119,211,229,
57,58,77,100,133,103,86,83,151,153,157,239,251,121,243,117,252,203,124,166,26,115,37,41,239,233,241,45,24,163,241,49,131,
145,20,237,128,159,155,225,51,165,88,143,150,101,157,184,222,118,189,3,215,117,76,222,227,89,17,167,19,126,188,202,140,
174,183,205,221,48,121,5,111,60,119,68,172,33,158,154,173,89,206,160,182,223,29,243,250,59,230,245,121,55,138,168,89,220,
161,186,144,179,226,94,213,148,28,17,109,151,208,102,91,122,93,84,47,218,58,151,89,209,231,232,170,205,170,216,252,126,
202,42,61,151,155,177,179,202,175,139,204,123,28,215,9,119,19,27,38,99,184,88,44,82,132,95,139,196,10,180,162,80,47,217,
135,142,88,190,227,206,12,122,110,104,187,225,65,223,107,56,101,219,223,116,212,106,88,196,138,164,193,85,231,254,102,81,
252,168,183,104,185,101,223,115,202,249,146,28,146,159,55,180,159,86,220,200,101,194,170,212,237,160,159,214,255,195,193,
183,131,252,158,32,176,195,253,78,197,30,178,131,146,239,212,66,15,177,150,182,92,203,86,104,77,91,129,157,31,172,251,
129,215,54,77,203,52,106,133,190,115,162,233,144,109,57,184,118,152,63,236,59,115,195,121,65,158,207,53,54,29,216,126,
131,103,221,59,215,116,208,242,75,118,101,126,50,59,139,37,175,154,247,189,138,147,63,138,210,229,111,92,191,213,77,161,
153,203,189,255,127,104,123,122,27,254,115,128,126,90,89,44,91,149,134,115,44,111,185,174,23,90,161,227,185,249,125,110,
169,226,5,220,187,98,5,216,131,158,5,124,134,93,23,25,75,123,239,2,246,81,187,58,173,28,248,54,118,22,249,41,201,87,44,
119,38,63,54,125,212,46,133,237,186,67,33,207,174,159,210,237,197,160,174,133,86,72,108,130,244,137,97,156,184,137,2,25,
19,5,33,225,236,77,20,113,112,39,138,5,28,92,126,29,38,54,73,139,167,22,152,37,105,149,74,118,16,236,175,88,51,1,69,248,
98,109,74,150,188,74,189,234,222,111,85,237,128,150,170,195,198,171,214,204,101,144,187,149,169,167,205,52,55,173,125,13,
168,105,89,155,253,62,59,196,164,182,85,29,63,89,67,220,155,218,140,99,53,219,229,1,168,179,77,253,64,221,246,79,146,89,
182,43,118,104,83,196,22,97,151,204,216,225,66,39,141,210,51,237,83,68,209,231,18,25,71,188,32,164,56,191,142,123,135,
177,66,211,113,145,104,72,70,197,43,29,35,163,106,5,199,40,93,117,170,54,119,71,212,16,149,53,170,94,25,67,93,84,129,98,
158,59,136,184,200,33,234,185,114,113,29,30,82,110,221,124,240,104,174,192,168,89,225,17,74,212,124,143,239,45,14,0,69,
142,203,101,224,118,173,87,144,71,128,229,72,75,71,75,220,227,163,254,113,212,54,28,243,203,124,246,240,136,19,144,201,
175,171,251,200,172,215,202,124,118,189,238,59,252,82,161,72,131,63,21,200,20,77,64,155,244,3,219,215,71,211,27,119,109,
164,187,40,154,222,53,73,203,140,3,219,7,118,72,213,42,173,111,32,154,158,196,147,24,38,178,244,194,208,190,104,154,30,
99,90,97,39,20,14,205,176,2,250,227,90,97,20,205,16,156,168,170,21,238,22,166,134,20,138,58,254,116,108,156,26,193,147,
119,36,50,178,103,104,223,126,97,157,50,10,163,34,150,214,193,70,186,83,90,90,91,107,100,239,94,114,75,83,88,166,45,98,
35,183,106,221,137,238,36,105,26,195,159,238,103,115,145,211,167,141,75,49,237,81,141,76,246,93,140,171,53,174,142,157,
57,109,60,30,103,80,39,216,133,56,49,35,110,104,73,232,46,9,93,147,69,236,199,56,99,127,129,139,9,198,62,0,95,129,171,
224,124,146,177,31,193,75,41,249,110,75,234,89,222,108,155,223,30,252,57,223,252,254,208,105,246,27,196,160,217,239,16,
222,54,191,69,76,154,253,30,209,51,82,230,127,207,88,78,190,75,15,64,54,115,82,207,223,161,88,70,190,103,139,119,228,156,
156,151,127,191,232,202,159,191,243,24,57,57,31,127,47,34,53,86,188,123,101,100,174,252,91,233,111,138,244,241,33,100,13,
0,0};
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (authority, "authority", "Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/os/ParcelFileDescriptor;JJ)V") \
METHOD (createInputStream, "createInputStream", "()Ljava/io/FileInputStream;") \
METHOD (getLength, "getLength", "()J")
@@ -47,28 +100,13 @@ DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo")
DECLARE_JNI_CLASS (AssetFileDescriptor, "android/content/res/AssetFileDescriptor")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V")
DECLARE_JNI_CLASS (JavaCloseable, "java/io/Closeable")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH ";JLjava/lang/String;I)V") \
METHOD (startWatching, "startWatching", "()V") \
METHOD (stopWatching, "stopWatching", "()V")
DECLARE_JNI_CLASS (JuceContentProviderFileObserver, JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH "$ProviderFileObserver")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH ";J[Ljava/lang/String;)V")
DECLARE_JNI_CLASS (JuceContentProviderFileObserverCursor, JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH "$ProviderCursor")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (open, "open", "(Ljava/io/File;I)Landroid/os/ParcelFileDescriptor;")
DECLARE_JNI_CLASS (ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
@@ -90,8 +128,8 @@ public:
const LocalRef<jobject>& contentProvider,
const LocalRef<jobjectArray>& resultColumns)
: owner (ownerToUse),
cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderFileObserverCursor,
JuceContentProviderFileObserverCursor.constructor,
cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderCursor,
JuceContentProviderCursor.constructor,
contentProvider.get(),
reinterpret_cast<jlong> (this),
resultColumns.get()))))
@@ -107,11 +145,35 @@ public:
MessageManager::callAsync ([this] { owner.cursorClosed (*this); });
}
void addRow (LocalRef<jobjectArray>& values)
{
auto* env = getEnv();
env->CallVoidMethod (cursor.get(), JuceContentProviderCursor.addRow, values.get());
}
private:
Owner& owner;
GlobalRef cursor;
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;J[Ljava/lang/String;)V") \
CALLBACK (contentSharerCursorClosed, "contentSharerCursorClosed", "(J)V") \
DECLARE_JNI_CLASS (JuceContentProviderCursor, "com/roli/juce/JuceSharingContentProvider$ProviderCursor")
#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
{
@@ -182,8 +244,26 @@ private:
Owner& owner;
String filepath;
GlobalRef fileObserver;
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;JLjava/lang/String;I)V") \
METHOD (startWatching, "startWatching", "()V") \
METHOD (stopWatching, "stopWatching", "()V") \
CALLBACK (contentSharerFileObserverEvent, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \
DECLARE_JNI_CLASS (JuceContentProviderFileObserver, "com/roli/juce/JuceSharingContentProvider$ProviderFileObserver")
#undef JNI_CLASS_MEMBERS
static void JNICALL contentSharerFileObserverEvent (JNIEnv*, jobject /*fileObserver*/, jlong host, int event, jstring path)
{
if (auto* myself = reinterpret_cast<AndroidContentSharerFileObserver*> (host))
myself->onFileEvent (event, LocalRef<jstring> (path));
}
};
AndroidContentSharerFileObserver::JuceContentProviderFileObserver_Class AndroidContentSharerFileObserver::JuceContentProviderFileObserver;
//==============================================================================
class AndroidContentSharerPrepareFilesThread : private Thread
{
@@ -220,7 +300,7 @@ public:
private:
struct StreamCloser
{
StreamCloser (jobject streamToUse)
StreamCloser (const LocalRef<jobject>& streamToUse)
: stream (GlobalRef (streamToUse))
{
}
@@ -289,7 +369,7 @@ private:
URL copyAssetFileToTemporaryFile (JNIEnv* env, const String& filename)
{
auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
auto resources = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources));
int fileId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (filename).get(),
javaString ("raw").get(), javaString (packageName).get());
@@ -386,8 +466,7 @@ class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl,
public:
ContentSharerNativeImpl (ContentSharer& cs)
: owner (cs),
packageName (juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (android.activity,
JuceAppActivity.getPackageName)))),
packageName (juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName)))),
uriBase ("content://" + packageName + ".sharingcontentprovider/")
{
}
@@ -431,7 +510,14 @@ public:
auto chooserIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent, AndroidIntent.createChooser,
intent.get(), javaString ("Choose share target").get()));
env->CallVoidMethod (android.activity, JuceAppActivity.startActivityForResult, chooserIntent.get(), 1003);
WeakReference<ContentSharerNativeImpl> weakRef (this);
startAndroidActivityForResult (chooserIntent, 1003,
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
{
if (weakRef != nullptr)
weakRef->sharingFinished (resultCode);
});
}
//==============================================================================
@@ -446,7 +532,7 @@ public:
}
//==============================================================================
void* openFile (const LocalRef<jobject>& contentProvider,
jobject openFile (const LocalRef<jobject>& contentProvider,
const LocalRef<jobject>& uri, const LocalRef<jstring>& mode)
{
ignoreUnused (mode);
@@ -466,7 +552,7 @@ public:
return getAssetFileDescriptor (env, contentProvider, uriElements.filepath);
}
void* query (const LocalRef<jobject>& contentProvider, const LocalRef<jobject>& uri,
jobject query (const LocalRef<jobject>& contentProvider, const LocalRef<jobject>& uri,
const LocalRef<jobjectArray>& projection, const LocalRef<jobject>& selection,
const LocalRef<jobjectArray>& selectionArgs, const LocalRef<jobject>& sortOrder)
{
@@ -521,13 +607,11 @@ public:
}
}
auto nativeCursor = cursor->getNativeCursor();
env->CallVoidMethod (nativeCursor, JuceContentProviderFileObserverCursor.addRow, values.get());
return nativeCursor;
cursor->addRow (values);
return cursor->getNativeCursor();
}
void* getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
jobjectArray getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
{
auto* env = getEnv();
@@ -558,8 +642,7 @@ private:
{
auto* env = getEnv();
auto packageManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getPackageManager));
LocalRef<jobject> packageManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
constexpr int getProviders = 8;
auto packageInfo = LocalRef<jobject> (env->CallObjectMethod (packageManager,
@@ -620,8 +703,14 @@ private:
AndroidIntent.createChooser,
intent.get(),
javaString ("Choose share target").get()));
WeakReference<ContentSharerNativeImpl> weakRef (this);
env->CallVoidMethod (android.activity, JuceAppActivity.startActivityForResult, chooserIntent.get(), 1003);
startAndroidActivityForResult (chooserIntent, 1003,
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
{
if (weakRef != nullptr)
weakRef->sharingFinished (resultCode);
});
}
void decrementPendingFileCountAndNotifyOwnerIfReady()
@@ -674,7 +763,7 @@ private:
return StringArray ("_display_name", "_size");
}
void* getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider,
jobject getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider,
const String& filepath)
{
// This function can be called from multiple threads.
@@ -740,89 +829,59 @@ private:
WeakReference<ContentSharerNativeImpl>::Master masterReference;
friend class WeakReference<ContentSharerNativeImpl>;
};
//==============================================================================
ContentSharer::Pimpl* ContentSharer::createPimpl()
{
return new ContentSharerNativeImpl (*this);
}
//==============================================================================
void* juce_contentSharerQuery (void* contentProvider, void* uri, void* projection,
void* selection, void* selectionArgs, void* sortOrder)
{
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
return pimpl->query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jobjectArray> (static_cast<jobjectArray> (projection)),
LocalRef<jobject> (static_cast<jobject> (selection)),
LocalRef<jobjectArray> (static_cast<jobjectArray> (selectionArgs)),
LocalRef<jobject> (static_cast<jobject> (sortOrder)));
}
void* juce_contentSharerOpenFile (void* contentProvider, void* uri, void* mode)
{
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
return pimpl->openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jstring> (static_cast<jstring> (mode)));
}
void juce_contentSharingCompleted (int resultCode)
{
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
return pimpl->sharingFinished (resultCode);
}
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
CALLBACK (contentSharerQuery, "contentSharerQuery", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
CALLBACK (contentSharerOpenFile, "contentSharerOpenFile", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;") \
CALLBACK (contentSharerGetStreamTypes, "contentSharerGetStreamTypes", "(Landroid/net/Uri;Ljava/lang/String;)[Ljava/lang/String;") \
void* juce_contentSharerGetStreamTypes (void* uri, void* mimeTypeFilter)
{
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
return pimpl->getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
}
//==============================================================================
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerFileObserverEvent, void,
(JNIEnv* env, jobject /*fileObserver*/, jlong host, int event, jstring path))
{
setEnv (env);
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceSharingContentProvider, "com/roli/juce/JuceSharingContentProvider", 16, javaJuceSharingContentProvider, sizeof(javaJuceSharingContentProvider))
#undef JNI_CLASS_MEMBERS
reinterpret_cast<AndroidContentSharerFileObserver*> (host)->onFileEvent (event, LocalRef<jstring> (path));
}
static jobject JNICALL contentSharerQuery (JNIEnv*, jobject contentProvider, jobject uri, jobjectArray projection,
jobject selection, jobjectArray selectionArgs, jobject sortOrder)
{
if (auto *pimpl = (ContentSharer::ContentSharerNativeImpl *) ContentSharer::getInstance ()->pimpl.get ())
return pimpl->query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jobjectArray> (
static_cast<jobjectArray> (projection)),
LocalRef<jobject> (static_cast<jobject> (selection)),
LocalRef<jobjectArray> (
static_cast<jobjectArray> (selectionArgs)),
LocalRef<jobject> (static_cast<jobject> (sortOrder)));
return nullptr;
}
//==============================================================================
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerQuery, jobject,
(JNIEnv* env, jobject contentProvider, jobject uri, jobjectArray projection,
jobject selection, jobjectArray selectionArgs, jobject sortOrder))
{
setEnv (env);
static jobject JNICALL contentSharerOpenFile (JNIEnv*, jobject contentProvider, jobject uri, jstring mode)
{
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
return pimpl->openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jstring> (static_cast<jstring> (mode)));
return (jobject) juce_contentSharerQuery (contentProvider, uri, projection, selection, selectionArgs, sortOrder);
}
return nullptr;
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerCursorClosed, void,
(JNIEnv* env, jobject /*cursor*/, jlong host))
{
setEnv (env);
static jobjectArray JNICALL contentSharerGetStreamTypes (JNIEnv*, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter)
{
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
return pimpl->getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
reinterpret_cast<AndroidContentSharerCursor*> (host)->cursorClosed();
}
return nullptr;
}
};
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerOpenFile, jobject,
(JNIEnv* env, jobject contentProvider, jobject uri, jstring mode))
//==============================================================================
ContentSharer::Pimpl* ContentSharer::createPimpl()
{
setEnv (env);
return (jobject) juce_contentSharerOpenFile ((void*) contentProvider, (void*) uri, (void*) mode);
return new ContentSharerNativeImpl (*this);
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerGetStreamTypes, jobject,
(JNIEnv* env, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter))
{
setEnv (env);
return (jobject) juce_contentSharerGetStreamTypes ((void*) uri, (void*) mimeTypeFilter);
}
ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider;
} // namespace juce

+ 29
- 14
modules/juce_gui_basics/native/juce_android_FileChooser.cpp View File

@@ -30,6 +30,7 @@ namespace juce
class FileChooser::Native : public FileChooser::Pimpl
{
public:
//==============================================================================
Native (FileChooser& fileChooser, int flags) : owner (fileChooser)
{
if (currentFileChooser == nullptr)
@@ -37,7 +38,7 @@ public:
currentFileChooser = this;
auto* env = getEnv();
auto sdkVersion = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion);
auto sdkVersion = getAndroidSDKVersion();
auto saveMode = ((flags & FileBrowserComponent::saveMode) != 0);
auto selectsDirectories = ((flags & FileBrowserComponent::canSelectDirectories) != 0);
@@ -64,8 +65,8 @@ public:
: "android.intent.action.GET_CONTENT")));
intent = GlobalRef (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
javaString (action).get()));
intent = GlobalRef (LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
javaString (action).get())));
if (owner.startingFile != File())
{
@@ -135,6 +136,7 @@ public:
~Native()
{
masterReference.clear();
currentFileChooser = nullptr;
}
@@ -146,13 +148,26 @@ public:
void launch() override
{
auto* env = getEnv();
if (currentFileChooser != nullptr)
android.activity.callVoidMethod (JuceAppActivity.startActivityForResult, intent.get(), /*READ_REQUEST_CODE*/ 42);
{
WeakReference<Native> myself (this);
startAndroidActivityForResult (LocalRef<jobject> (env->NewLocalRef (intent.get())), /*READ_REQUEST_CODE*/ 42,
[myself] (int requestCode, int resultCode, LocalRef<jobject> intentData) mutable
{
if (myself != nullptr)
myself->onActivityResult (requestCode, resultCode, intentData);
});
}
else
{
jassertfalse; // There is already a file chooser running
}
}
void completed (int resultCode, jobject intentData)
void onActivityResult (int /*requestCode*/, int resultCode, const LocalRef<jobject>& intentData)
{
currentFileChooser = nullptr;
auto* env = getEnv();
@@ -161,7 +176,7 @@ public:
if (resultCode == /*Activity.RESULT_OK*/ -1 && intentData != nullptr)
{
LocalRef<jobject> uri (env->CallObjectMethod (intentData, AndroidIntent.getData));
LocalRef<jobject> uri (env->CallObjectMethod (intentData.get(), AndroidIntent.getData));
if (uri != nullptr)
{
@@ -197,23 +212,23 @@ public:
}
private:
JUCE_DECLARE_WEAK_REFERENCEABLE (Native)
FileChooser& owner;
GlobalRef intent;
};
FileChooser::Native* FileChooser::Native::currentFileChooser = nullptr;
void juce_fileChooserCompleted (int resultCode, void* intentData)
{
if (FileChooser::Native::currentFileChooser != nullptr)
FileChooser::Native::currentFileChooser->completed (resultCode, (jobject) intentData);
}
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent*)
{
return new FileChooser::Native (owner, flags);
if (FileChooser::Native::currentFileChooser == nullptr)
return new FileChooser::Native (owner, flags);
// there can only be one file chooser on Android at a once
jassertfalse;
return nullptr;
}
bool FileChooser::isPlatformDialogAvailable()


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


+ 2
- 8
modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h View File

@@ -46,12 +46,8 @@ class JUCE_API AndroidViewComponent : public Component
{
public:
//==============================================================================
/** Create an initially-empty container. The optional flag should be left as
false in most of the cases. Currently it is only set to true as a workaround
for a web browser bug, where scrolling would be very slow and it would
randomly scroll in an opposite direction of scrolling.
*/
AndroidViewComponent (bool embedAsSiblingRatherThanChild = false);
/** Create an initially-empty container */
AndroidViewComponent();
/** Destructor. */
~AndroidViewComponent();
@@ -77,8 +73,6 @@ private:
class Pimpl;
std::unique_ptr<Pimpl> pimpl;
bool embedAsSiblingRatherThanChild;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidViewComponent)
};


+ 2
- 6
modules/juce_gui_extra/misc/juce_WebBrowserComponent.h View File

@@ -134,10 +134,10 @@ public:
void visibilityChanged() override;
/** @internal */
void focusGained (FocusChangeType) override;
/** @internal */
class Pimpl;
private:
//==============================================================================
class Pimpl;
std::unique_ptr<Pimpl> browser;
bool blankPageShown = false, unloadPageWhenBrowserIsHidden;
String lastURL;
@@ -147,10 +147,6 @@ private:
void reloadLastURL();
void checkWindowAssociation();
#if JUCE_ANDROID
friend bool juce_webViewPageLoadStarted (WebBrowserComponent*, const String&);
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
};


+ 107
- 0
modules/juce_gui_extra/native/java/com/roli/juce/JuceWebView.java View File

@@ -0,0 +1,107 @@
package com.roli.juce;

import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Message;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;


//==============================================================================
public class JuceWebView
{
static public class Client extends WebViewClient
{
public Client (long hostToUse)
{
host = hostToUse;
}

public void hostDeleted ()
{
synchronized (hostLock)
{
host = 0;
}
}

public void onPageFinished (WebView view, String url)
{
if (host == 0)
return;

webViewPageLoadFinished (host, view, url);
}

public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
{
if (host == 0)
return;

webViewReceivedSslError (host, view, handler, error);
}

public void onPageStarted (WebView view, String url, Bitmap favicon)
{
if (host != 0)
webViewPageLoadStarted (host, view, url);
}

public WebResourceResponse shouldInterceptRequest (WebView view, String url)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, url);

if (shouldLoad)
return null;
}
}

return new WebResourceResponse ("text/html", null, null);
}

private native boolean webViewPageLoadStarted (long host, WebView view, String url);

private native void webViewPageLoadFinished (long host, WebView view, String url);

private native void webViewReceivedSslError (long host, WebView view, SslErrorHandler handler, SslError error);

private long host;
private final Object hostLock = new Object ();
}

static public class ChromeClient extends WebChromeClient
{
public ChromeClient (long hostToUse)
{
host = hostToUse;
}

@Override
public void onCloseWindow (WebView window)
{
webViewCloseWindowRequest (host, window);
}

@Override
public boolean onCreateWindow (WebView view, boolean isDialog,
boolean isUserGesture, Message resultMsg)
{
webViewCreateWindowRequest (host, view);
return false;
}

private native void webViewCloseWindowRequest (long host, WebView view);

private native void webViewCreateWindowRequest (long host, WebView view);

private long host;
private final Object hostLock = new Object ();
}
}

+ 12
- 26
modules/juce_gui_extra/native/juce_AndroidViewComponent.cpp View File

@@ -30,11 +30,10 @@ namespace juce
class AndroidViewComponent::Pimpl : public ComponentMovementWatcher
{
public:
Pimpl (jobject v, Component& comp, bool makeSiblingRatherThanChild = false)
Pimpl (const LocalRef<jobject>& v, Component& comp)
: ComponentMovementWatcher (&comp),
view (v),
owner (comp),
embedAsSiblingRatherThanChild (makeSiblingRatherThanChild)
owner (comp)
{
if (owner.isShowing())
componentPeerChanged();
@@ -68,7 +67,6 @@ public:
if (currentPeer != peer)
{
removeFromParent();
currentPeer = peer;
addToParent();
@@ -91,10 +89,6 @@ public:
void componentBroughtToFront (Component& comp) override
{
ComponentMovementWatcher::componentBroughtToFront (comp);
// Ensure that the native component doesn't get obscured.
if (embedAsSiblingRatherThanChild)
getEnv()->CallVoidMethod (view, AndroidView.bringToFront);
}
Rectangle<int> getViewBounds() const
@@ -119,20 +113,7 @@ private:
// NB: Assuming a parent is always of ViewGroup type
auto* env = getEnv();
if (embedAsSiblingRatherThanChild)
{
// This is a workaround for a bug in a web browser component where
// scrolling would be very slow and occasionally would scroll in
// opposite direction to dragging direction. In normal circumstances,
// the native view should be a child of peerView instead.
auto parentView = LocalRef<jobject> (env->CallObjectMethod (peerView, AndroidView.getParent));
env->CallVoidMethod (parentView, AndroidViewGroup.addView, view.get());
}
else
{
env->CallVoidMethod (peerView, AndroidViewGroup.addView, view.get());
}
env->CallVoidMethod (peerView, AndroidViewGroup.addView, view.get());
componentMovedOrResized (false, false);
}
}
@@ -150,15 +131,13 @@ private:
}
Component& owner;
bool embedAsSiblingRatherThanChild;
ComponentPeer* currentPeer = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
//==============================================================================
AndroidViewComponent::AndroidViewComponent (bool makeSiblingRatherThanChild)
: embedAsSiblingRatherThanChild (makeSiblingRatherThanChild)
AndroidViewComponent::AndroidViewComponent()
{
}
@@ -171,7 +150,14 @@ void AndroidViewComponent::setView (void* view)
pimpl.reset();
if (view != nullptr)
pimpl.reset (new Pimpl ((jobject) view, *this, embedAsSiblingRatherThanChild));
{
// explicitly create a new local ref here so that we don't
// delete the users pointer
auto* env = getEnv();
auto localref = LocalRef<jobject>(env->NewLocalRef((jobject) view));
pimpl.reset (new Pimpl (localref, *this));
}
}
}


+ 210
- 232
modules/juce_gui_extra/native/juce_android_PushNotifications.cpp View File

@@ -27,8 +27,7 @@
namespace juce
{
#if __ANDROID_API__ >= 26
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
METHOD (enableLights, "enableLights", "(Z)V") \
METHOD (enableVibration, "enableVibration", "(Z)V") \
@@ -42,36 +41,31 @@ namespace juce
METHOD (setSound, "setSound", "(Landroid/net/Uri;Landroid/media/AudioAttributes;)V") \
METHOD (setVibrationPattern, "setVibrationPattern", "([J)V")
DECLARE_JNI_CLASS (NotificationChannel, "android/app/NotificationChannel")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationChannel, "android/app/NotificationChannel", 26)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;)V")
DECLARE_JNI_CLASS (NotificationChannelGroup, "android/app/NotificationChannelGroup")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationChannelGroup, "android/app/NotificationChannelGroup", 26)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 19
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (extras, "extras", "Landroid/os/Bundle;")
DECLARE_JNI_CLASS (AndroidNotification, "android/app/Notification")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidNotification, "android/app/Notification", 19)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 20
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addExtras, "addExtras", "(Landroid/os/Bundle;)Landroid/app/Notification$Action$Builder;") \
METHOD (addRemoteInput, "addRemoteInput", "(Landroid/app/RemoteInput;)Landroid/app/Notification$Action$Builder;") \
METHOD (constructor, "<init>", "(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)V") \
METHOD (build, "build", "()Landroid/app/Notification$Action;")
DECLARE_JNI_CLASS (NotificationActionBuilder, "android/app/Notification$Action$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationActionBuilder, "android/app/Notification$Action$Builder", 20)
#undef JNI_CLASS_MEMBERS
#endif
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getNotification, "getNotification", "()Landroid/app/Notification;") \
METHOD (setAutoCancel, "setAutoCancel", "(Z)Landroid/app/Notification$Builder;") \
METHOD (setContentInfo, "setContentInfo", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
@@ -92,31 +86,26 @@ DECLARE_JNI_CLASS (NotificationActionBuilder, "android/app/Notification$Action$B
METHOD (setVibrate, "setVibrate", "([J)Landroid/app/Notification$Builder;") \
METHOD (setWhen, "setWhen", "(J)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderBase, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderBase, "android/app/Notification$Builder", 11)
#undef JNI_CLASS_MEMBERS
#if __ANDROID_API__ >= 16
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addAction, "addAction", "(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;") \
METHOD (build, "build", "()Landroid/app/Notification;") \
METHOD (setPriority, "setPriority", "(I)Landroid/app/Notification$Builder;") \
METHOD (setSubText, "setSubText", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
METHOD (setUsesChronometer, "setUsesChronometer", "(Z)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi16, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi16, "android/app/Notification$Builder", 16)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 17
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (setShowWhen, "setShowWhen", "(Z)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi17, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi17, "android/app/Notification$Builder", 17)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 20
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addAction, "addAction", "(Landroid/app/Notification$Action;)Landroid/app/Notification$Builder;") \
METHOD (addExtras, "addExtras", "(Landroid/os/Bundle;)Landroid/app/Notification$Builder;") \
METHOD (setLocalOnly, "setLocalOnly", "(Z)Landroid/app/Notification$Builder;") \
@@ -124,41 +113,34 @@ DECLARE_JNI_CLASS (NotificationBuilderApi17, "android/app/Notification$Builder")
METHOD (setGroupSummary, "setGroupSummary", "(Z)Landroid/app/Notification$Builder;") \
METHOD (setSortKey, "setSortKey", "(Ljava/lang/String;)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi20, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi20, "android/app/Notification$Builder", 20)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 21
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (addPerson, "addPerson", "(Ljava/lang/String;)Landroid/app/Notification$Builder;") \
METHOD (setCategory, "setCategory", "(Ljava/lang/String;)Landroid/app/Notification$Builder;") \
METHOD (setColor, "setColor", "(I)Landroid/app/Notification$Builder;") \
METHOD (setPublicVersion, "setPublicVersion", "(Landroid/app/Notification;)Landroid/app/Notification$Builder;") \
METHOD (setVisibility, "setVisibility", "(I)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi21, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi21, "android/app/Notification$Builder", 21)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 24
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (setChronometerCountDown, "setChronometerCountDown", "(Z)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi24, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi24, "android/app/Notification$Builder", 24)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 26
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (setBadgeIconType, "setBadgeIconType", "(I)Landroid/app/Notification$Builder;") \
METHOD (setGroupAlertBehavior, "setGroupAlertBehavior", "(I)Landroid/app/Notification$Builder;") \
METHOD (setTimeoutAfter, "setTimeoutAfter", "(J)Landroid/app/Notification$Builder;")
DECLARE_JNI_CLASS (NotificationBuilderApi26, "android/app/Notification$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationBuilderApi26, "android/app/Notification$Builder", 26)
#undef JNI_CLASS_MEMBERS
#endif
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (cancel, "cancel", "(Ljava/lang/String;I)V") \
METHOD (cancelAll, "cancelAll", "()V") \
METHOD (notify, "notify", "(Ljava/lang/String;ILandroid/app/Notification;)V")
@@ -166,56 +148,46 @@ DECLARE_JNI_CLASS (NotificationBuilderApi26, "android/app/Notification$Builder")
DECLARE_JNI_CLASS (NotificationManagerBase, "android/app/NotificationManager")
#undef JNI_CLASS_MEMBERS
#if __ANDROID_API__ >= 23
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getActiveNotifications, "getActiveNotifications", "()[Landroid/service/notification/StatusBarNotification;")
DECLARE_JNI_CLASS (NotificationManagerApi23, "android/app/NotificationManager")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationManagerApi23, "android/app/NotificationManager", 23)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 24
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (areNotificationsEnabled, "areNotificationsEnabled", "()Z")
DECLARE_JNI_CLASS (NotificationManagerApi24, "android/app/NotificationManager")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationManagerApi24, "android/app/NotificationManager", 24)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 26
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (createNotificationChannel, "createNotificationChannel", "(Landroid/app/NotificationChannel;)V") \
METHOD (createNotificationChannelGroup, "createNotificationChannelGroup", "(Landroid/app/NotificationChannelGroup;)V")
DECLARE_JNI_CLASS (NotificationManagerApi26, "android/app/NotificationManager")
DECLARE_JNI_CLASS_WITH_MIN_SDK (NotificationManagerApi26, "android/app/NotificationManager", 26)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 20
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getResultsFromIntent, "getResultsFromIntent", "(Landroid/content/Intent;)Landroid/os/Bundle;")
DECLARE_JNI_CLASS (RemoteInput, "android/app/RemoteInput")
DECLARE_JNI_CLASS_WITH_MIN_SDK (RemoteInput, "android/app/RemoteInput", 20)
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (build, "build", "()Landroid/app/RemoteInput;") \
METHOD (setAllowFreeFormInput, "setAllowFreeFormInput", "(Z)Landroid/app/RemoteInput$Builder;") \
METHOD (setChoices, "setChoices", "([Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;") \
METHOD (setLabel, "setLabel", "(Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;")
DECLARE_JNI_CLASS (RemoteInputBuilder, "android/app/RemoteInput$Builder")
DECLARE_JNI_CLASS_WITH_MIN_SDK (RemoteInputBuilder, "android/app/RemoteInput$Builder", 20)
#undef JNI_CLASS_MEMBERS
#endif
#if __ANDROID_API__ >= 23
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getNotification, "getNotification", "()Landroid/app/Notification;")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (getNotification, "getNotification", "()Landroid/app/Notification;")
DECLARE_JNI_CLASS (StatusBarNotification, "android/service/notification/StatusBarNotification")
DECLARE_JNI_CLASS_WITH_MIN_SDK (StatusBarNotification, "android/service/notification/StatusBarNotification", 23)
#undef JNI_CLASS_MEMBERS
#endif
//==========================================================================
#if defined(JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME)
@@ -284,10 +256,8 @@ DECLARE_JNI_CLASS (RemoteInputBuilder, "android/app/RemoteInput$Builder")
//==============================================================================
bool PushNotifications::Notification::isValid() const noexcept
{
auto* env = getEnv();
bool isValidForPreApi26 = title.isNotEmpty() && body.isNotEmpty() && identifier.isNotEmpty() && icon.isNotEmpty();
bool apiAtLeast26 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 26;
bool apiAtLeast26 = (getAndroidSDKVersion() >= 26);
if (apiAtLeast26)
return isValidForPreApi26 && channelId.isNotEmpty();
@@ -304,18 +274,17 @@ struct PushNotifications::Pimpl
bool areNotificationsEnabled() const
{
#if __ANDROID_API__ >= 24
auto* env = getEnv();
if (getAndroidSDKVersion() >= 24)
{
auto* env = getEnv();
auto notificationManager = getNotificationManager();
auto notificationManager = getNotificationManager();
if (notificationManager.get() != 0)
return env->CallBooleanMethod (notificationManager, NotificationManagerApi24.areNotificationsEnabled);
if (notificationManager.get() != 0)
return env->CallBooleanMethod (notificationManager, NotificationManagerApi24.areNotificationsEnabled);
}
return false;
#else
return true;
#endif
}
//==========================================================================
@@ -341,48 +310,52 @@ struct PushNotifications::Pimpl
void getDeliveredNotifications() const
{
#if __ANDROID_API__ >= 23
auto* env = getEnv();
if (getAndroidSDKVersion() >= 23)
{
auto* env = getEnv();
Array<PushNotifications::Notification> notifications;
Array<PushNotifications::Notification> notifications;
auto notificationManager = getNotificationManager();
auto notificationManager = getNotificationManager();
jassert (notificationManager.get() != 0);
jassert (notificationManager.get() != 0);
if (notificationManager.get() != 0)
{
auto statusBarNotifications = LocalRef<jobjectArray> ((jobjectArray)env->CallObjectMethod (notificationManager,
NotificationManagerApi23.getActiveNotifications));
if (notificationManager.get() != 0)
{
auto statusBarNotifications = LocalRef<jobjectArray> ((jobjectArray)env->CallObjectMethod (notificationManager,
NotificationManagerApi23.getActiveNotifications));
const int numNotifications = env->GetArrayLength (statusBarNotifications.get());
const int numNotifications = env->GetArrayLength (statusBarNotifications.get());
for (int i = 0; i < numNotifications; ++i)
{
auto statusBarNotification = LocalRef<jobject> (env->GetObjectArrayElement (statusBarNotifications.get(), (jsize) i));
auto notification = LocalRef<jobject> (env->CallObjectMethod (statusBarNotification, StatusBarNotification.getNotification));
for (int i = 0; i < numNotifications; ++i)
{
auto statusBarNotification = LocalRef<jobject> (env->GetObjectArrayElement (statusBarNotifications.get(), (jsize) i));
auto notification = LocalRef<jobject> (env->CallObjectMethod (statusBarNotification, StatusBarNotification.getNotification));
notifications.add (javaNotificationToJuceNotification (notification));
notifications.add (javaNotificationToJuceNotification (notification));
}
}
}
owner.listeners.call ([&] (Listener& l) { l.deliveredNotificationsListReceived (notifications); });
#else
// Not supported on this platform
jassertfalse;
owner.listeners.call ([] (Listener& l) { l.deliveredNotificationsListReceived ({}); });
#endif
owner.listeners.call ([&] (Listener& l) { l.deliveredNotificationsListReceived (notifications); });
}
else
{
// Not supported on this platform
jassertfalse;
owner.listeners.call ([] (Listener& l) { l.deliveredNotificationsListReceived ({}); });
}
}
void notifyListenersAboutLocalNotification (const LocalRef<jobject>& intent)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, AndroidIntent.getExtras));
const auto notification = localNotificationBundleToJuceNotification (bundle);
auto packageName = juceString ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
auto packageName = juceString ((jstring) env->CallObjectMethod (context.get(), AndroidContext.getPackageName));
String notificationString = packageName + ".JUCE_NOTIFICATION.";
String notificationButtonActionString = packageName + ".JUCE_NOTIFICATION_BUTTON_ACTION.";
@@ -403,8 +376,7 @@ struct PushNotifications::Pimpl
owner.listeners.call ([&] (Listener& l) { l.handleNotificationAction (true, notification, actionTitle, {}); });
}
#if __ANDROID_API__ >= 20
else if (actionString.contains (notificationTextInputActionString))
else if (getAndroidSDKVersion() >= 20 && actionString.contains (notificationTextInputActionString))
{
auto prefix = notificationTextInputActionString + notification.identifier + ".";
@@ -426,7 +398,6 @@ struct PushNotifications::Pimpl
owner.listeners.call ([&] (Listener& l) { l.handleNotificationAction (true, notification, actionTitle, responseString); });
}
#endif
}
void notifyListenersAboutLocalNotificationDeleted (const LocalRef<jobject>& intent)
@@ -632,9 +603,10 @@ struct PushNotifications::Pimpl
static LocalRef<jobject> getNotificationManager()
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
return LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getSystemService,
return LocalRef<jobject> (env->CallObjectMethod (context.get(),
AndroidContext.getSystemService,
javaString ("notification").get()));
}
@@ -650,16 +622,16 @@ struct PushNotifications::Pimpl
if (n.actions.size() > 0)
setupActions (n, notificationBuilder);
#if __ANDROID_API__ >= 16
return LocalRef<jobject> (env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.build));
#else
if (getAndroidSDKVersion() >= 16)
return LocalRef<jobject> (env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.build));
return LocalRef<jobject> (env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.getNotification));
#endif
}
static LocalRef<jobject> createNotificationBuilder (const PushNotifications::Notification& n)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
jclass builderClass = env->FindClass ("android/app/Notification$Builder");
jassert (builderClass != 0);
@@ -669,7 +641,7 @@ struct PushNotifications::Pimpl
jmethodID builderConstructor = 0;
const bool apiAtLeast26 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 26;
const bool apiAtLeast26 = (getAndroidSDKVersion() >= 26);
if (apiAtLeast26)
builderConstructor = env->GetMethodID (builderClass, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
@@ -683,19 +655,20 @@ struct PushNotifications::Pimpl
if (apiAtLeast26)
return LocalRef<jobject> (env->NewObject (builderClass, builderConstructor,
android.activity.get(), javaString (n.channelId).get()));
context.get(), javaString (n.channelId).get()));
return LocalRef<jobject> (env->NewObject (builderClass, builderConstructor, android.activity.get()));
return LocalRef<jobject> (env->NewObject (builderClass, builderConstructor, context.get()));
}
static void setupRequiredFields (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, JavaObject.getClass));
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
auto actionStringSuffix = javaString (".JUCE_NOTIFICATION." + n.identifier);
auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
@@ -705,7 +678,7 @@ struct PushNotifications::Pimpl
auto notifyPendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidPendingIntent,
AndroidPendingIntent.getActivity,
android.activity.get(),
context.get(),
1002,
notifyIntent.get(),
0));
@@ -714,14 +687,13 @@ struct PushNotifications::Pimpl
env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentText, javaString (n.body).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentIntent, notifyPendingIntent.get());
auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
auto resources = LocalRef<jobject> (env->CallObjectMethod (context.get(), AndroidContext.getResources));
const int iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (n.icon).get(),
javaString ("raw").get(), packageNameString.get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setSmallIcon, iconId);
#if __ANDROID_API__ >= 21
if (n.publicVersion != nullptr)
if (getAndroidSDKVersion() >= 21 && n.publicVersion != nullptr)
{
// Public version of a notification is not expected to have another public one!
jassert (n.publicVersion->publicVersion == nullptr);
@@ -734,7 +706,6 @@ struct PushNotifications::Pimpl
auto publicVersion = LocalRef<jobject> (env->CallObjectMethod (publicNotificationBuilder, NotificationBuilderApi16.build));
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setPublicVersion, publicVersion.get());
}
#endif
}
static LocalRef<jobject> juceNotificationToBundle (const PushNotifications::Notification& n)
@@ -852,70 +823,77 @@ struct PushNotifications::Pimpl
env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setOngoing, n.ongoing);
env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setOnlyAlertOnce, n.alertOnlyOnce);
#if __ANDROID_API__ >= 16
if (n.subtitle.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setSubText, javaString (n.subtitle).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setPriority, n.priority);
if (getAndroidSDKVersion() >= 16)
{
if (n.subtitle.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setSubText, javaString (n.subtitle).get());
#if __ANDROID_API__ < 24
const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer);
#endif
#endif
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setPriority, n.priority);
#if __ANDROID_API__ >= 17
const bool showTimeStamp = n.timestampVisibility != PushNotifications::Notification::off;
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi17.setShowWhen, showTimeStamp);
#endif
if (getAndroidSDKVersion() < 24)
{
const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer);
}
}
#if __ANDROID_API__ >= 20
if (n.groupId.isNotEmpty())
if (getAndroidSDKVersion() >= 17)
{
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroup, javaString (n.groupId).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroupSummary, n.groupSummary);
const bool showTimeStamp = n.timestampVisibility != PushNotifications::Notification::off;
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi17.setShowWhen, showTimeStamp);
}
if (n.groupSortKey.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setSortKey, javaString (n.groupSortKey).get());
if (getAndroidSDKVersion() >= 20)
{
if (n.groupId.isNotEmpty())
{
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroup, javaString (n.groupId).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroupSummary, n.groupSummary);
}
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setLocalOnly, n.localOnly);
if (n.groupSortKey.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setSortKey, javaString (n.groupSortKey).get());
auto extras = LocalRef<jobject> (env->NewObject (AndroidBundle, AndroidBundle.constructor));
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setLocalOnly, n.localOnly);
env->CallVoidMethod (extras, AndroidBundle.putBundle, javaString ("notificationData").get(),
juceNotificationToBundle (n).get());
auto extras = LocalRef<jobject> (env->NewObject (AndroidBundle, AndroidBundle.constructor));
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addExtras, extras.get());
#endif
env->CallVoidMethod (extras, AndroidBundle.putBundle, javaString ("notificationData").get(),
juceNotificationToBundle (n).get());
#if __ANDROID_API__ >= 21
if (n.person.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.addPerson, javaString (n.person).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addExtras, extras.get());
}
auto categoryString = typeToCategory (n.type);
if (categoryString.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setCategory, javaString (categoryString).get());
if (getAndroidSDKVersion() >= 21)
{
if (n.person.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.addPerson, javaString (n.person).get());
if (n.accentColour != Colour())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setColor, n.accentColour.getARGB());
auto categoryString = typeToCategory (n.type);
if (categoryString.isNotEmpty())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setCategory, javaString (categoryString).get());
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setVisibility, n.lockScreenAppearance);
#endif
if (n.accentColour != Colour())
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setColor, n.accentColour.getARGB());
#if __ANDROID_API__ >= 24
const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
const bool useCountDownChronometer = n.timestampVisibility == PushNotifications::Notification::countDownChronometer;
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setVisibility, n.lockScreenAppearance);
}
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi24.setChronometerCountDown, useCountDownChronometer);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer | useCountDownChronometer);
#endif
if (getAndroidSDKVersion() >= 24)
{
const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
const bool useCountDownChronometer = n.timestampVisibility == PushNotifications::Notification::countDownChronometer;
#if __ANDROID_API__ >= 26
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setBadgeIconType, n.badgeIconType);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setGroupAlertBehavior, n.groupAlertBehaviour);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setTimeoutAfter, (jlong) n.timeoutAfterMs);
#endif
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi24.setChronometerCountDown, useCountDownChronometer);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer | useCountDownChronometer);
}
if (getAndroidSDKVersion() >= 26)
{
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setBadgeIconType, n.badgeIconType);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setGroupAlertBehavior, n.groupAlertBehaviour);
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setTimeoutAfter, (jlong) n.timeoutAfterMs);
}
setupNotificationDeletedCallback (n, notificationBuilder);
}
@@ -924,11 +902,12 @@ struct PushNotifications::Pimpl
LocalRef<jobject>& notificationBuilder)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, JavaObject.getClass));
auto deleteIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
auto deleteIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
auto actionStringSuffix = javaString (".JUCE_NOTIFICATION_DELETED." + n.identifier);
auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
@@ -937,7 +916,7 @@ struct PushNotifications::Pimpl
auto deletePendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidPendingIntent,
AndroidPendingIntent.getActivity,
android.activity.get(),
context.get(),
1002,
deleteIntent.get(),
0));
@@ -947,19 +926,22 @@ struct PushNotifications::Pimpl
static void setupActions (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
{
#if __ANDROID_API__ >= 16
if (getAndroidSDKVersion() < 16)
return;
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
int actionIndex = 0;
for (const auto& action : n.actions)
{
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, JavaObject.getClass));
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
const bool isTextStyle = action.style == PushNotifications::Notification::Action::text;
auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
const String notificationActionString = isTextStyle ? ".JUCE_NOTIFICATION_TEXT_INPUT_ACTION." : ".JUCE_NOTIFICATION_BUTTON_ACTION.";
auto actionStringSuffix = javaString (notificationActionString + n.identifier + "." + String (actionIndex) + "." + action.title);
auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
@@ -970,12 +952,12 @@ struct PushNotifications::Pimpl
auto notifyPendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidPendingIntent,
AndroidPendingIntent.getActivity,
android.activity.get(),
context.get(),
1002,
notifyIntent.get(),
0));
auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
auto resources = LocalRef<jobject> (env->CallObjectMethod (context.get(), AndroidContext.getResources));
int iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (action.icon).get(),
javaString ("raw").get(), packageNameString.get());
@@ -983,70 +965,71 @@ struct PushNotifications::Pimpl
iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (n.icon).get(),
javaString ("raw").get(), packageNameString.get());
#if __ANDROID_API__ >= 20
auto actionBuilder = LocalRef<jobject> (env->NewObject (NotificationActionBuilder,
NotificationActionBuilder.constructor,
iconId,
javaString (action.title).get(),
notifyPendingIntent.get()));
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addExtras,
varToBundleWithPropertiesString (action.parameters).get());
if (isTextStyle)
if (getAndroidSDKVersion() >= 20)
{
auto resultKey = javaString (action.title + String (actionIndex));
auto remoteInputBuilder = LocalRef<jobject> (env->NewObject (RemoteInputBuilder,
RemoteInputBuilder.constructor,
resultKey.get()));
auto actionBuilder = LocalRef<jobject> (env->NewObject (NotificationActionBuilder,
NotificationActionBuilder.constructor,
iconId,
javaString (action.title).get(),
notifyPendingIntent.get()));
if (! action.textInputPlaceholder.isEmpty())
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setLabel, javaString (action.textInputPlaceholder).get());
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addExtras,
varToBundleWithPropertiesString (action.parameters).get());
if (! action.allowedResponses.isEmpty())
if (isTextStyle)
{
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setAllowFreeFormInput, false);
auto resultKey = javaString (action.title + String (actionIndex));
auto remoteInputBuilder = LocalRef<jobject> (env->NewObject (RemoteInputBuilder,
RemoteInputBuilder.constructor,
resultKey.get()));
const int size = action.allowedResponses.size();
if (! action.textInputPlaceholder.isEmpty())
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setLabel, javaString (action.textInputPlaceholder).get());
auto array = LocalRef<jobjectArray> (env->NewObjectArray (size, env->FindClass ("java/lang/String"), 0));
for (int i = 0; i < size; ++i)
if (! action.allowedResponses.isEmpty())
{
const auto& response = action.allowedResponses[i];
auto responseString = javaString (response);
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setAllowFreeFormInput, false);
const int size = action.allowedResponses.size();
auto array = LocalRef<jobjectArray> (env->NewObjectArray (size, env->FindClass ("java/lang/String"), 0));
env->SetObjectArrayElement (array, i, responseString.get());
for (int i = 0; i < size; ++i)
{
const auto& response = action.allowedResponses[i];
auto responseString = javaString (response);
env->SetObjectArrayElement (array, i, responseString.get());
}
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setChoices, array.get());
}
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setChoices, array.get());
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addRemoteInput,
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.build));
}
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addRemoteInput,
env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.build));
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addAction,
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.build));
}
else
{
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.addAction,
iconId, javaString (action.title).get(), notifyPendingIntent.get());
}
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addAction,
env->CallObjectMethod (actionBuilder, NotificationActionBuilder.build));
#else
env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.addAction,
iconId, javaString (action.title).get(), notifyPendingIntent.get());
#endif
++actionIndex;
}
#else
ignoreUnused (n, notificationBuilder);
#endif // #if __ANDROID_API__ >= 16
}
static LocalRef<jobject> juceUrlToAndroidUri (const URL& url)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
auto resources = LocalRef<jobject> (env->CallObjectMethod (context.get(), AndroidContext.getResources));
const int id = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (url.toString (true)).get(),
javaString ("raw").get(), packageNameString.get());
@@ -1270,7 +1253,9 @@ struct PushNotifications::Pimpl
static PushNotifications::Notification javaNotificationToJuceNotification (const LocalRef<jobject>& notification)
{
#if __ANDROID_API__ >= 20
if (getAndroidSDKVersion() < 20)
return {};
auto* env = getEnv();
auto extras = LocalRef<jobject> (env->GetObjectField (notification, AndroidNotification.extras));
@@ -1279,12 +1264,8 @@ struct PushNotifications::Pimpl
if (notificationData.get() != nullptr)
return localNotificationBundleToJuceNotification (notificationData);
else
return remoteNotificationBundleToJuceNotification (extras);
#else
ignoreUnused (notification);
return {};
#endif
return remoteNotificationBundleToJuceNotification (extras);
}
static PushNotifications::Notification remoteNotificationBundleToJuceNotification (const LocalRef<jobject>& bundle)
@@ -1429,12 +1410,11 @@ struct PushNotifications::Pimpl
void setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
{
#if __ANDROID_API__ >= 26
auto* env = getEnv();
if (env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) < 26)
if (getAndroidSDKVersion() < 26)
return;
auto* env = getEnv();
auto notificationManager = getNotificationManager();
jassert (notificationManager.get() != 0);
@@ -1503,9 +1483,6 @@ struct PushNotifications::Pimpl
env->CallVoidMethod (notificationManager, NotificationManagerApi26.createNotificationChannel, channel.get());
}
#else
ignoreUnused (groups, channels);
#endif
}
void getPendingLocalNotifications() const {}
@@ -1515,9 +1492,10 @@ struct PushNotifications::Pimpl
static bool intentActionContainsAnyOf (jobject intent, const StringArray& strings, bool includePackageName)
{
auto* env = getEnv();
LocalRef<jobject> context (getAppContext());
String packageName = includePackageName ? juceString ((jstring) env->CallObjectMethod (android.activity,
JuceAppActivity.getPackageName))
String packageName = includePackageName ? juceString ((jstring) env->CallObjectMethod (context.get(),
AndroidContext.getPackageName))
: String{};
String intentAction = juceString ((jstring) env->CallObjectMethod (intent, AndroidIntent.getAction));


+ 242
- 126
modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp View File

@@ -27,7 +27,122 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
//==============================================================================
// This byte-code is generated from native/java/com/roli/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[] =
{31,139,8,8,167,106,229,91,0,3,74,117,99,101,87,101,98,86,105,101,119,49,54,46,100,101,120,0,125,150,93,108,84,69,20,199,
207,253,216,189,187,183,237,178,109,161,31,20,74,91,177,84,164,44,88,145,210,5,172,124,83,22,63,186,80,100,53,145,219,
221,177,189,101,123,239,114,239,221,182,38,68,81,155,168,4,19,99,225,141,23,19,36,38,248,136,9,15,62,16,35,209,24,141,47,
70,125,224,193,23,141,15,62,136,49,132,104,31,252,207,157,217,101,129,98,55,191,61,103,206,57,51,115,102,230,116,118,10,
108,206,220,52,176,133,222,95,60,244,253,183,71,91,94,92,240,223,75,222,186,115,227,231,111,110,221,252,44,186,176,245,
181,235,117,68,37,34,154,27,123,178,137,228,223,162,73,52,76,194,190,156,75,133,168,1,242,58,164,14,249,178,74,212,12,
121,26,82,227,49,248,202,196,136,2,200,250,40,218,160,7,172,7,253,224,105,176,7,188,2,78,131,15,193,85,240,35,88,4,45,6,
209,83,224,4,152,7,31,129,27,224,87,16,195,184,107,192,0,216,15,70,192,179,32,11,142,131,19,160,0,108,224,2,15,204,129,
55,192,57,176,0,46,130,143,193,21,112,13,124,1,190,3,63,129,223,192,31,224,95,80,23,39,234,0,235,193,78,112,16,88,96,26,
204,129,215,193,219,224,44,248,0,92,1,95,129,95,192,29,208,104,138,253,192,146,8,169,19,134,36,152,9,102,194,54,83,61,
137,125,76,128,101,32,9,26,1,223,248,102,185,215,43,64,11,104,5,107,64,68,142,119,41,34,108,149,67,106,147,250,167,176,
183,75,253,42,244,14,169,127,14,125,165,212,191,134,190,74,234,63,64,239,148,250,77,232,171,165,126,169,198,254,123,141,
254,55,244,46,153,31,31,167,91,234,60,41,190,182,222,112,141,73,234,147,235,236,13,165,104,71,72,33,17,106,134,50,38,219,
113,82,165,140,82,127,40,235,105,99,40,53,26,146,50,29,142,35,226,76,244,91,23,202,24,165,66,25,167,77,161,52,104,179,
156,119,32,148,17,218,30,202,58,218,17,74,157,118,134,123,47,230,77,86,231,167,80,139,200,189,228,53,29,160,113,89,164,
25,142,167,200,243,171,248,231,225,255,82,250,235,164,63,89,227,63,15,255,95,210,207,179,158,135,126,214,188,171,47,152,
162,207,69,147,199,107,161,222,110,138,122,40,37,185,175,7,227,149,146,124,207,95,74,42,148,107,18,117,162,99,4,62,126,
175,41,234,32,139,195,40,13,71,73,221,156,192,234,35,161,175,223,20,245,38,124,6,124,77,97,125,85,230,217,90,157,71,189,
111,30,13,243,168,225,60,226,172,20,218,99,138,58,61,242,140,70,171,149,22,164,159,219,165,82,167,146,192,8,157,202,58,
89,143,10,62,113,204,169,133,237,195,166,168,231,236,176,74,188,7,206,68,221,6,95,34,180,148,198,18,164,191,208,247,15,
223,79,61,140,31,51,197,218,106,227,7,49,154,136,94,134,232,4,246,88,15,215,123,194,20,245,150,45,61,48,182,167,146,113,
202,152,55,46,24,151,103,162,252,108,251,110,243,179,225,57,169,52,133,126,143,240,26,86,142,188,133,149,168,217,121,244,
199,128,155,53,61,58,168,213,19,111,151,114,203,232,192,5,147,6,49,87,167,218,168,116,170,61,106,148,86,106,91,176,139,
10,61,209,104,116,247,221,110,128,117,157,188,247,154,49,71,119,184,75,252,211,37,37,238,26,83,248,197,174,38,194,123,
176,246,239,204,125,237,115,247,181,121,141,24,184,9,148,154,54,183,232,85,169,146,38,245,70,89,123,252,188,181,170,183,
162,139,49,184,222,136,207,50,89,155,6,50,111,134,53,186,221,118,236,96,39,213,239,158,244,220,105,182,187,104,51,39,160,
168,148,202,8,37,71,202,121,118,140,141,143,217,108,118,227,148,53,99,145,150,201,100,168,61,99,57,5,207,181,11,169,9,
207,42,77,218,121,63,181,203,14,166,173,82,154,58,170,46,135,5,169,201,32,40,165,178,126,113,175,231,185,94,154,150,87,
157,174,159,58,204,124,223,154,96,105,234,170,90,103,217,248,73,59,168,118,56,0,123,145,121,75,68,32,165,218,148,211,180,
118,137,136,81,230,187,101,47,207,32,75,174,227,99,166,182,37,162,248,210,210,212,249,16,79,101,252,190,76,222,157,78,
121,110,209,78,77,97,75,82,53,251,178,246,222,76,122,254,47,82,198,116,60,60,134,15,80,176,138,51,246,201,148,229,56,110,
96,5,182,235,164,246,58,249,162,235,219,206,196,238,162,229,251,60,221,7,99,14,58,14,243,164,191,123,9,255,97,54,61,46,3,
24,66,86,100,248,121,166,108,23,29,75,229,32,27,120,204,154,78,83,147,48,23,45,103,34,245,220,248,20,203,7,247,218,16,
135,52,210,164,140,145,58,54,66,218,216,72,134,116,124,101,40,194,191,51,176,102,96,205,112,43,111,42,57,210,115,161,59,
151,201,229,50,84,103,229,243,56,248,125,69,107,194,167,8,227,199,76,198,171,214,140,157,119,29,50,38,197,137,147,62,233,
250,1,213,241,239,61,172,200,2,86,160,24,111,100,220,252,73,138,115,237,136,123,212,103,20,179,253,61,182,85,116,39,168,
193,246,97,240,246,51,63,40,123,140,116,199,154,102,212,224,58,187,177,111,236,152,237,20,220,89,74,160,137,85,6,53,237,
231,81,129,251,240,79,224,79,98,138,6,209,206,6,150,199,103,108,114,157,81,150,103,246,12,43,84,42,146,226,30,243,203,
197,224,176,63,65,45,254,164,91,46,22,14,58,1,67,145,149,130,81,118,170,140,217,201,20,246,140,107,21,40,30,176,57,254,
95,48,93,36,61,152,180,125,210,202,94,145,34,51,86,177,140,28,103,112,222,212,62,91,169,180,106,162,149,145,86,86,92,53,
73,87,124,173,210,199,19,230,83,85,23,209,114,159,163,178,154,74,135,7,150,20,157,21,187,49,170,172,55,18,106,107,90,157,
154,237,167,227,202,176,145,200,209,9,125,232,241,13,3,92,107,14,189,219,210,234,1,120,219,201,72,236,56,212,185,138,186,
213,161,65,35,113,118,21,110,209,161,193,71,141,196,59,57,122,76,27,234,93,27,218,182,114,231,234,29,239,78,105,180,101,
121,127,119,132,58,214,44,224,50,52,18,164,214,43,131,109,117,106,131,218,163,199,55,180,42,21,69,85,19,202,224,42,181,
45,222,134,31,122,77,37,85,105,210,222,60,163,159,55,180,183,240,59,5,116,229,186,161,40,55,241,131,166,71,84,120,99,240,
46,26,81,233,229,196,149,79,98,136,0,231,226,138,114,13,252,25,231,247,99,35,34,111,154,149,223,103,165,70,14,147,120,
215,242,59,179,242,182,229,247,101,237,251,182,242,198,141,208,221,119,110,148,238,190,117,181,164,208,249,61,175,116,
137,247,194,121,232,209,46,105,71,71,37,41,236,252,93,165,118,137,121,249,219,88,147,241,252,183,95,239,146,111,11,110,
144,125,195,55,72,82,228,202,223,225,255,1,121,75,63,47,192,11,0,0};
//==============================================================================
// This byte-code is generated from native/javacore/app/com/roli/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[] =
{31,139,8,8,20,250,226,91,0,3,74,117,99,101,87,101,98,86,105,101,119,50,49,46,100,101,120,0,149,151,93,108,28,87,21,199,
207,124,236,206,174,119,118,179,222,117,28,219,113,236,181,235,54,78,147,116,227,36,37,78,54,169,204,58,113,237,48,81,
193,78,220,104,145,144,198,187,131,61,206,122,102,51,51,187,142,196,3,161,141,0,209,10,169,130,190,81,41,15,41,170,10,21,
66,66,34,60,240,134,74,16,47,149,120,160,128,84,1,2,169,15,17,42,15,84,81,37,36,254,247,99,54,27,199,68,176,171,223,158,
115,207,57,115,239,61,247,158,153,189,211,112,110,244,29,59,241,60,93,89,253,237,71,111,149,22,172,113,253,199,127,126,
238,195,175,252,238,212,236,173,175,189,250,151,87,111,127,146,37,106,17,209,141,213,147,5,146,159,35,176,45,145,176,15,
130,239,43,68,204,121,31,82,135,252,149,74,52,4,249,55,72,13,242,14,126,238,167,225,131,179,101,160,47,240,77,240,93,240,
6,120,27,188,3,222,3,247,192,159,192,3,144,75,17,29,3,203,96,27,188,5,126,14,126,15,52,244,119,8,44,130,38,120,29,252,4,
188,15,254,8,254,1,254,9,254,5,62,3,212,71,100,128,12,200,131,65,48,12,198,193,51,224,4,56,3,150,192,50,168,129,58,112,
65,7,220,4,175,129,55,193,29,240,46,248,5,248,13,248,3,248,24,252,27,244,103,136,70,192,211,224,28,88,2,151,65,13,52,128,
11,90,160,3,222,0,239,129,247,193,71,224,1,232,55,197,154,33,125,66,154,36,167,78,112,17,92,132,165,167,28,216,3,242,160,
159,196,218,23,193,0,216,43,247,100,31,137,61,24,6,35,96,18,36,128,42,247,48,41,251,255,56,249,80,255,36,41,98,138,50,
102,68,246,201,62,251,165,254,0,49,163,177,29,147,28,147,122,170,71,31,128,126,64,234,37,232,227,82,63,2,125,66,234,39,
123,244,57,232,37,169,179,57,196,118,171,39,230,42,244,167,100,126,172,207,41,169,55,12,177,54,135,249,26,21,232,168,92,
167,195,92,138,182,138,21,125,129,231,172,147,156,54,29,226,57,239,225,237,62,105,207,240,236,153,236,163,227,92,246,211,
9,46,147,84,149,114,158,247,43,226,76,92,119,132,203,44,157,228,50,71,207,115,105,210,231,184,204,208,41,46,21,58,195,
165,42,101,154,206,115,153,167,11,92,166,104,129,75,131,94,228,123,44,230,83,232,206,139,208,131,216,39,246,97,214,187,
104,252,218,36,57,15,225,239,235,241,223,131,255,239,210,159,149,254,66,143,255,67,248,199,179,162,205,106,226,109,196,
254,200,124,168,255,204,20,215,252,210,100,241,26,215,135,77,209,87,43,175,160,61,137,254,90,121,86,103,95,70,187,86,16,
117,169,163,7,214,255,51,166,152,239,10,54,182,53,151,34,117,38,135,236,18,220,119,212,20,123,32,124,105,248,10,124,39,
226,113,78,117,199,209,119,140,163,97,28,149,143,147,224,145,10,45,152,34,255,203,159,215,232,128,50,136,233,215,170,42,
141,41,57,244,48,166,28,228,59,149,36,54,223,52,198,212,120,251,37,83,220,63,43,115,42,177,43,102,144,246,105,248,114,
220,210,90,197,186,127,105,250,51,86,31,58,143,191,106,138,220,122,227,103,209,155,136,46,34,58,135,61,210,121,190,107,
166,184,127,86,90,143,245,29,168,100,92,55,110,25,111,26,63,236,36,7,48,163,233,79,169,123,93,243,127,188,110,111,247,58,
150,139,74,29,83,212,122,65,185,252,10,86,64,93,185,133,235,209,225,140,166,39,103,181,1,98,237,32,175,162,54,76,205,203,
179,103,139,169,181,86,138,180,136,7,217,44,198,30,83,251,149,49,117,82,77,209,136,118,22,187,161,209,241,126,99,98,250,
211,44,172,7,229,243,124,63,198,60,196,87,155,125,167,187,99,191,102,138,123,114,247,177,77,62,118,171,246,255,141,85,
196,8,19,221,177,74,82,18,221,54,133,95,84,66,142,255,151,244,126,242,59,218,163,59,218,172,174,89,5,8,89,228,125,198,
118,102,209,165,76,32,183,88,55,80,1,154,212,139,242,30,98,207,99,173,27,25,235,6,127,126,106,61,125,107,60,151,34,127,
62,235,220,190,23,95,236,245,89,215,115,163,23,200,156,223,8,252,45,103,190,233,58,94,68,73,41,149,139,84,184,216,174,59,
47,59,107,171,174,179,125,124,230,185,77,187,99,147,98,145,102,89,22,13,91,182,215,8,124,183,81,94,15,236,214,134,91,15,
203,85,55,218,178,91,21,234,239,186,60,39,42,95,9,220,10,237,127,196,180,17,69,173,242,74,216,188,16,4,126,80,161,129,
174,211,15,203,151,156,48,180,215,157,10,149,186,214,109,103,237,154,27,117,47,88,132,189,233,4,187,68,96,170,189,169,84,
232,169,93,34,150,157,208,111,7,117,103,217,185,222,118,66,4,77,61,49,40,108,249,94,136,233,12,237,18,197,214,165,66,99,
255,197,19,79,226,89,171,238,111,149,3,191,233,150,55,177,158,229,71,22,117,234,209,9,79,61,57,86,70,29,120,82,84,133,38,
173,134,221,236,184,215,202,182,231,249,145,29,185,190,87,190,224,213,155,126,232,122,235,243,77,59,12,217,164,31,143,89,
242,60,39,144,254,137,93,252,151,156,173,53,25,224,32,100,175,197,10,162,236,250,184,176,213,142,86,162,192,177,183,42,
84,16,230,166,237,173,151,95,90,219,116,234,209,163,54,196,97,26,21,82,86,73,93,189,72,218,234,69,139,116,252,88,148,96,
191,22,172,40,177,85,139,89,89,83,169,145,94,227,238,154,85,171,89,148,177,235,117,212,200,66,211,94,15,41,225,176,138,
160,44,23,241,102,145,241,85,187,227,214,125,143,146,235,78,116,37,104,146,177,33,106,134,244,13,63,140,40,195,126,207,
59,77,39,114,26,148,98,13,203,175,95,163,52,211,46,251,87,208,67,202,13,207,187,118,211,95,167,172,27,194,16,188,136,82,
105,7,14,233,158,189,229,80,214,247,230,177,156,206,203,174,215,240,183,41,135,38,146,143,122,218,95,68,13,47,224,246,10,
55,48,68,86,180,87,34,59,96,35,22,125,111,217,169,59,110,199,105,44,226,78,224,69,77,133,135,198,184,208,201,8,68,141,82,
58,112,194,118,51,186,20,174,211,96,184,225,183,155,141,37,47,114,80,159,173,72,150,49,245,9,187,229,219,13,74,71,206,13,
118,151,109,53,73,143,54,220,144,82,145,47,150,157,180,54,150,35,209,177,155,109,228,210,65,193,208,240,118,92,174,221,
132,226,62,71,98,87,79,114,177,111,159,244,177,196,216,160,221,100,7,119,56,226,172,135,164,253,241,212,247,237,240,116,
243,79,110,139,245,220,84,202,70,78,29,172,168,215,182,143,210,117,101,201,200,213,232,134,94,61,54,115,134,105,3,220,
251,1,85,212,159,126,29,254,17,50,114,231,190,48,54,74,147,106,117,206,200,125,103,148,142,107,213,185,67,70,238,91,53,
186,170,85,79,79,115,219,179,90,245,240,65,174,45,170,213,211,198,129,115,223,254,171,70,103,7,143,78,36,104,255,248,247,
104,150,93,11,227,102,175,113,15,122,38,53,167,204,13,101,213,61,234,211,122,122,102,159,18,43,170,154,87,230,70,213,161,
204,16,78,86,154,74,170,82,72,124,227,166,126,39,165,189,162,146,2,146,202,7,41,69,185,143,211,71,194,80,225,237,131,247,
245,116,74,122,99,50,202,221,52,162,192,15,250,20,229,30,184,153,81,148,187,64,28,130,6,113,213,109,118,30,201,203,255,
12,165,71,198,239,65,236,127,36,126,23,98,207,254,222,247,161,248,157,136,157,37,226,247,162,36,61,124,55,210,242,66,103,
255,105,74,73,156,103,166,160,39,75,194,206,206,122,74,94,156,137,216,121,93,45,201,113,113,136,210,100,60,59,155,233,37,
49,22,59,191,145,188,150,159,17,243,98,174,236,189,237,63,5,143,187,196,240,13,0,0};
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;)V") \
METHOD (getSettings, "getSettings", "()Landroid/webkit/WebSettings;") \
METHOD (goBack, "goBack", "()V") \
@@ -43,38 +158,25 @@ namespace juce
DECLARE_JNI_CLASS (AndroidWebView, "android/webkit/WebView")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V")
DECLARE_JNI_CLASS (AndroidWebChromeClient, "android/webkit/WebChromeClient")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V")
DECLARE_JNI_CLASS (AndroidWebViewClient, "android/webkit/WebViewClient")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getInstance, "getInstance", "()Landroid/webkit/CookieManager;")
DECLARE_JNI_CLASS (AndroidCookieManager, "android/webkit/CookieManager")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH ";J)V")
DECLARE_JNI_CLASS (JuceWebChromeClient, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceWebChromeClient")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH ";J)V") \
METHOD (hostDeleted, "hostDeleted", "()V")
DECLARE_JNI_CLASS (JuceWebViewClient, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceWebViewClient")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (setBuiltInZoomControls, "setBuiltInZoomControls", "(Z)V") \
METHOD (setDisplayZoomControls, "setDisplayZoomControls", "(Z)V") \
METHOD (setJavaScriptEnabled, "setJavaScriptEnabled", "(Z)V") \
@@ -83,13 +185,13 @@ DECLARE_JNI_CLASS (JuceWebViewClient, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceWebV
DECLARE_JNI_CLASS (WebSettings, "android/webkit/WebSettings")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (SslError, "android/net/http/SslError")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (encode, "encode", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
DECLARE_JNI_CLASS (URLEncoder, "java/net/URLEncoder")
@@ -101,12 +203,11 @@ class WebBrowserComponent::Pimpl : public AndroidViewComponent,
{
public:
Pimpl (WebBrowserComponent& o)
: AndroidViewComponent (true),
owner (o)
: owner (o)
{
auto* env = getEnv();
setView (env->NewObject (AndroidWebView, AndroidWebView.constructor, android.activity.get()));
setView (env->NewObject (AndroidWebView, AndroidWebView.constructor, getMainActivity().get()));
auto settings = LocalRef<jobject> (env->CallObjectMethod ((jobject) getView(), AndroidWebView.getSettings));
env->CallVoidMethod (settings, WebSettings.setJavaScriptEnabled, true);
@@ -115,13 +216,18 @@ public:
env->CallVoidMethod (settings, WebSettings.setSupportMultipleWindows, true);
juceWebChromeClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebChromeClient, JuceWebChromeClient.constructor,
android.activity.get(),
reinterpret_cast<jlong>(&owner))));
reinterpret_cast<jlong> (this))));
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebChromeClient, juceWebChromeClient.get());
juceWebViewClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebViewClient, JuceWebViewClient.constructor,
android.activity.get(),
reinterpret_cast<jlong>(&owner))));
auto sdkVersion = getAndroidSDKVersion();
if (sdkVersion >= 21)
juceWebViewClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebViewClient21, JuceWebViewClient21.constructor,
reinterpret_cast<jlong> (this))));
else
juceWebViewClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebViewClient16, JuceWebViewClient16.constructor,
reinterpret_cast<jlong> (this))));
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebViewClient, juceWebViewClient.get());
}
@@ -144,7 +250,8 @@ public:
// the lock we need when calling hostDeleted.
responseReadyEvent.signal();
env->CallVoidMethod (juceWebViewClient, JuceWebViewClient.hostDeleted);
env->CallVoidMethod (juceWebViewClient, getAndroidSDKVersion() >= 21 ? JuceWebViewClient21.hostDeleted
: JuceWebViewClient16.hostDeleted);
}
void goToURL (const String& url,
@@ -281,6 +388,8 @@ public:
return shouldLoad;
}
WebBrowserComponent& owner;
private:
class ConnectionThread : private Thread
{
@@ -361,8 +470,107 @@ private:
Result result;
};
//==============================================================================
#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") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient21, "com/roli/juce/JuceWebView21$Client", 21, JuceWebView21ByteCode, sizeof (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") \
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceWebViewClient16, "com/roli/juce/JuceWebView$Client", 16, JuceWebView16ByteCode, sizeof (JuceWebView16ByteCode))
#undef JNI_CLASS_MEMBERS
static jboolean JNICALL webViewPageLoadStarted (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jstring url)
{
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host))
return myself->handlePageAboutToLoad (juceString (url));
WebBrowserComponent& owner;
return 0;
}
static void JNICALL webViewPageLoadFinished (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jstring url)
{
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host))
myself->owner.pageFinishedLoading (juceString (url));
}
static void JNICALL webViewReceivedHttpError (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject errorResponse)
{
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));
}
}
//==============================================================================
#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") \
DECLARE_JNI_CLASS (JuceWebChromeClient, "com/roli/juce/JuceWebView$ChromeClient")
#undef JNI_CLASS_MEMBERS
static void JNICALL webViewCloseWindowRequest (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/)
{
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host))
myself->owner.windowCloseRequest();
}
static void JNICALL webViewCreateWindowRequest (JNIEnv*, jobject /*activity*/, jlong host, jobject /*webView*/)
{
if (auto* myself = reinterpret_cast<WebBrowserComponent::Pimpl*> (host))
myself->owner.newWindowAttemptingToLoad ({});
}
//==============================================================================
void webReceivedHttpError (jobject errorResponse)
{
auto* env = getEnv();
LocalRef<jclass> responseClass (env->FindClass ("android/webkit/WebResourceResponse"));
if (responseClass != 0)
{
jmethodID method = env->GetMethodID (responseClass, "getReasonPhrase", "()Ljava/lang/String;");
if (method != 0)
{
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method));
owner.pageLoadHadNetworkError (juceString (errorString));
return;
}
}
// Should never get here!
jassertfalse;
owner.pageLoadHadNetworkError ({});
}
//==============================================================================
GlobalRef juceWebChromeClient;
GlobalRef juceWebViewClient;
std::unique_ptr<ConnectionThread> connectionThread;
@@ -496,11 +704,9 @@ void WebBrowserComponent::clearCookies()
auto cookieManager = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidCookieManager,
AndroidCookieManager.getInstance));
const bool apiAtLeast21 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 21;
jmethodID clearCookiesMethod = 0;
if (apiAtLeast21)
if (getAndroidSDKVersion() >= 21)
{
clearCookiesMethod = env->GetMethodID (AndroidCookieManager, "removeAllCookies", "(Landroid/webkit/ValueCallback;)V");
env->CallVoidMethod (cookieManager, clearCookiesMethod, 0);
@@ -512,97 +718,7 @@ void WebBrowserComponent::clearCookies()
}
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewPageLoadStarted, bool, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject url))
{
setEnv (env);
return juce_webViewPageLoadStarted (reinterpret_cast<WebBrowserComponent*> (host),
juceString (static_cast<jstring> (url)));
}
bool juce_webViewPageLoadStarted (WebBrowserComponent* browserComponent, const String& url)
{
return browserComponent->browser->handlePageAboutToLoad (url);
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewPageLoadFinished, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject url))
{
setEnv (env);
reinterpret_cast<WebBrowserComponent*> (host)->pageFinishedLoading (juceString (static_cast<jstring> (url)));
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject error))
{
setEnv (env);
jclass errorClass = env->FindClass ("android/webkit/WebResourceError");
if (errorClass != 0)
{
jmethodID method = env->GetMethodID (errorClass, "getDescription", "()Ljava/lang/CharSequence;");
if (method != 0)
{
auto sequence = LocalRef<jobject> (env->CallObjectMethod (error, method));
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sequence, JavaCharSequence.toString));
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
return;
}
}
// Should never get here!
jassertfalse;
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError ({});
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedHttpError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject errorResponse))
{
setEnv (env);
jclass responseClass = env->FindClass ("android/webkit/WebResourceResponse");
if (responseClass != 0)
{
jmethodID method = env->GetMethodID (responseClass, "getReasonPhrase", "()Ljava/lang/String;");
if (method != 0)
{
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method));
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
return;
}
}
// Should never get here!
jassertfalse;
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError ({});
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedSslError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*sslErrorHandler*/, jobject sslError))
{
setEnv (env);
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sslError, SslError.toString));
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewCloseWindowRequest, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/))
{
setEnv (env);
reinterpret_cast<WebBrowserComponent*> (host)->windowCloseRequest();
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewCreateWindowRequest, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/))
{
setEnv (env);
reinterpret_cast<WebBrowserComponent*> (host)->newWindowAttemptingToLoad ({});
}
WebBrowserComponent::Pimpl::JuceWebViewClient16_Class WebBrowserComponent::Pimpl::JuceWebViewClient16;
WebBrowserComponent::Pimpl::JuceWebViewClient21_Class WebBrowserComponent::Pimpl::JuceWebViewClient21;
WebBrowserComponent::Pimpl::JuceWebChromeClient_Class WebBrowserComponent::Pimpl::JuceWebChromeClient;
} // namespace juce

+ 56
- 0
modules/juce_opengl/native/java/com/roli/juce/JuceOpenGLView.java View File

@@ -0,0 +1,56 @@
package com.roli.juce;

import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceView;

public class JuceOpenGLView extends SurfaceView
{
private long host = 0;

JuceOpenGLView (Context context, long nativeThis)
{
super (context);
host = nativeThis;
}

public void cancel ()
{
host = 0;
}

//==============================================================================
@Override
protected void onAttachedToWindow ()
{
super.onAttachedToWindow ();

if (host != 0)
onAttchedWindowNative (host);
}

@Override
protected void onDetachedFromWindow ()
{
if (host != 0)
onDetachedFromWindowNative (host);

super.onDetachedFromWindow ();
}

@Override
protected void dispatchDraw (Canvas canvas)
{
super.dispatchDraw (canvas);

if (host != 0)
onDrawNative (host, canvas);
}

//==============================================================================
private native void onAttchedWindowNative (long nativeThis);

private native void onDetachedFromWindowNative (long nativeThis);

private native void onDrawNative (long nativeThis, Canvas canvas);
}

+ 1
- 2
modules/juce_opengl/native/juce_OpenGLExtensions.h View File

@@ -65,8 +65,7 @@ namespace juce
USE_FUNCTION (glUniformMatrix2fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4))\
USE_FUNCTION (glUniformMatrix3fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4))\
USE_FUNCTION (glUniformMatrix4fv, void, (GLint p1, GLsizei p2, GLboolean p3, const GLfloat* p4), (p1, p2, p3, p4))\
USE_FUNCTION (glBindAttribLocation, void, (GLuint p1, GLuint p2, const GLchar* p3), (p1, p2, p3))\
USE_FUNCTION (glDrawBuffers, void, (GLsizei p1, const GLenum* p2), (p1, p2))
USE_FUNCTION (glBindAttribLocation, void, (GLuint p1, GLuint p2, const GLchar* p3), (p1, p2, p3))
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux.
@see OpenGLExtensionFunctions


+ 113
- 53
modules/juce_opengl/native/juce_OpenGL_android.h View File

@@ -27,16 +27,56 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
METHOD (layout, "layout", "(IIII)V" ) \
METHOD (getNativeSurface, "getNativeSurface", "()Landroid/view/Surface;") \
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/JuceOpenGLView.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaJuceOpenGLView[] =
{31,139,8,8,110,106,229,91,0,3,74,117,99,101,79,112,101,110,71,76,86,105,101,119,46,100,101,120,0,109,84,63,104,19,81,24,
255,222,221,75,82,211,244,26,211,70,180,130,68,172,160,80,189,170,21,11,169,82,105,169,16,15,139,180,164,90,187,28,151,
211,92,105,239,98,114,77,139,56,20,151,58,185,40,34,34,184,56,22,167,14,213,77,165,187,179,58,56,58,43,82,7,7,127,239,79,
76,10,61,248,189,223,247,190,255,119,247,222,87,241,215,210,195,23,46,210,241,45,159,207,157,188,253,250,215,169,241,167,
83,63,159,111,216,239,78,127,114,30,230,63,252,229,68,53,34,90,43,143,228,72,63,11,208,29,38,165,79,2,159,53,255,0,24,48,
140,37,3,30,101,106,255,2,203,164,73,180,13,126,111,16,125,4,118,128,47,192,111,32,11,219,16,224,0,211,192,12,112,11,88,
0,92,32,0,238,3,15,128,199,192,19,224,37,240,22,216,1,190,2,187,64,130,171,122,16,9,34,37,116,95,41,141,132,238,191,75,
203,207,80,251,128,150,95,65,78,107,249,13,228,110,45,111,118,232,183,12,149,55,43,107,152,50,151,9,75,175,174,153,211,
220,47,235,115,105,23,107,70,178,242,227,218,143,163,211,62,189,239,215,251,188,100,131,14,105,125,235,93,196,99,104,30,
81,105,241,94,92,234,186,185,234,175,86,32,58,47,35,231,33,205,15,18,89,136,101,210,55,207,85,255,45,171,37,227,13,89,
227,40,150,131,224,168,192,104,150,102,198,225,133,180,231,80,112,20,223,76,236,107,227,221,196,111,90,242,27,48,217,207,
96,43,38,43,114,236,27,51,220,69,156,117,198,12,233,152,253,189,211,240,142,112,12,184,236,141,237,121,103,67,238,153,
222,183,229,228,88,16,6,241,21,98,37,234,43,173,120,254,116,205,15,175,57,229,192,95,61,187,232,54,93,58,226,184,97,165,
30,5,21,219,139,194,216,15,99,123,66,240,90,92,236,48,221,171,187,181,106,224,53,236,9,55,108,186,141,34,13,252,55,53,
145,201,158,89,169,223,117,61,95,100,45,210,49,199,139,150,237,122,180,20,216,139,40,104,239,173,90,36,86,38,163,92,34,
179,92,114,32,56,16,156,18,37,61,55,244,252,37,201,168,64,41,79,117,65,153,74,208,168,185,177,87,157,172,187,171,196,171,
81,35,166,116,232,198,65,211,159,173,6,13,202,69,225,213,56,118,189,170,95,153,141,230,130,176,18,173,82,94,234,132,74,
41,110,72,119,234,143,194,73,95,185,78,213,163,101,237,60,176,159,86,71,100,96,67,89,189,227,177,40,216,99,244,230,82,
214,165,233,51,56,152,41,235,58,13,49,43,101,93,222,152,167,1,82,124,2,188,113,103,12,63,128,227,240,241,245,117,190,109,
242,71,6,25,0,3,18,236,155,201,248,31,96,147,27,252,59,239,209,255,145,117,112,107,110,24,29,179,195,236,152,31,173,115,
47,102,72,130,218,115,36,73,237,89,194,10,202,38,230,9,203,182,239,178,81,80,249,197,140,49,181,143,184,31,84,80,177,35,
250,242,10,89,204,176,127,191,250,1,66,252,4,0,0};
//==============================================================================
struct AndroidGLCallbacks
{
static void attachedToWindow (JNIEnv*, jobject, jlong);
static void detachedFromWindow (JNIEnv*, jobject, jlong);
static void dispatchDraw (JNIEnv*, jobject, jlong, jobject);
};
DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView")
//==============================================================================
#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/roli/juce/JuceOpenGLView", 16, javaJuceOpenGLView, sizeof(javaJuceOpenGLView))
#undef JNI_CLASS_MEMBERS
//==============================================================================
class OpenGLContext::NativeContext
class OpenGLContext::NativeContext : private SurfaceHolderCallback
{
public:
NativeContext (Component& comp,
@@ -58,10 +98,10 @@ public:
return;
// create a native surface view
surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
JuceAppActivity.createNativeSurfaceView,
reinterpret_cast<jlong> (this),
false));
surfaceView = GlobalRef (LocalRef<jobject>(env->NewObject (JuceOpenGLViewSurface,
JuceOpenGLViewSurface.constructor,
getAppContext().get(),
reinterpret_cast<jlong> (this))));
if (surfaceView.get() == nullptr)
return;
@@ -82,7 +122,7 @@ public:
{
auto env = getEnv();
if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent))
if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getParent))
env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
}
@@ -98,15 +138,22 @@ public:
ANativeWindow* window = nullptr;
if (jobject jSurface = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getNativeSurface))
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (holder != nullptr)
{
window = ANativeWindow_fromSurface (env, jSurface);
LocalRef<jobject> jSurface (env->CallObjectMethod (holder.get(), AndroidSurfaceHolder.getSurface));
// if we didn't succeed the first time, wait briefly and try again..
if (window == nullptr)
if (jSurface != nullptr)
{
Thread::sleep (200);
window = ANativeWindow_fromSurface (env, jSurface);
window = ANativeWindow_fromSurface(env, jSurface.get());
// if we didn't succeed the first time, wait 25ms and try again
if (window == nullptr)
{
Thread::sleep (200);
window = ANativeWindow_fromSurface (env, jSurface.get());
}
}
}
@@ -188,29 +235,20 @@ public:
lastBounds = bounds;
auto r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale;
env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout,
env->CallVoidMethod (surfaceView.get(), JuceOpenGLViewSurface.layout,
(jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
}
}
//==============================================================================
// Android Surface Callbacks:
void dispatchDraw (jobject canvas)
{
ignoreUnused (canvas);
if (juceContext != nullptr)
juceContext->triggerRepaint();
}
void surfaceChanged (jobject holder, int format, int width, int height)
void surfaceChanged (LocalRef<jobject> holder, int format, int width, int height) override
{
ignoreUnused (holder, format, width, height);
}
void surfaceCreated (jobject holder);
void surfaceDestroyed (jobject holder);
void surfaceCreated (LocalRef<jobject> holder) override;
void surfaceDestroyed (LocalRef<jobject> holder) override;
//==============================================================================
struct Locker { Locker (NativeContext&) {} };
@@ -218,6 +256,40 @@ public:
Component& component;
private:
//==============================================================================
friend struct AndroidGLCallbacks;
void attachedToWindow()
{
auto* env = getEnv();
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (surfaceHolderCallback == nullptr)
surfaceHolderCallback = GlobalRef (CreateJavaInterface (this, "android/view/SurfaceHolder$Callback"));
env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, surfaceHolderCallback.get());
}
void detachedFromWindow()
{
if (surfaceHolderCallback != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, surfaceHolderCallback.get());
surfaceHolderCallback.clear();
}
}
void dispatchDraw (jobject /*canvas*/)
{
if (juceContext != nullptr)
juceContext->triggerRepaint();
}
//==============================================================================
bool initEGLDisplay()
{
@@ -271,6 +343,8 @@ private:
EGLSurface surface;
EGLContext context;
GlobalRef surfaceHolderCallback;
static EGLDisplay display;
static EGLConfig config;
@@ -278,36 +352,22 @@ private:
};
//==============================================================================
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), dispatchDrawNative,
void, (JNIEnv* env, jobject nativeView, jlong host, jobject canvas))
{
ignoreUnused (nativeView);
setEnv (env);
reinterpret_cast<OpenGLContext::NativeContext*> (host)->dispatchDraw (canvas);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceChangedNative,
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder, jint format, jint width, jint height))
void AndroidGLCallbacks::attachedToWindow (JNIEnv*, jobject /*this*/, jlong host)
{
ignoreUnused (nativeView);
setEnv (env);
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceChanged (holder, format, width, height);
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->attachedToWindow();
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceCreatedNative,
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
void AndroidGLCallbacks::detachedFromWindow (JNIEnv*, jobject /*this*/, jlong host)
{
ignoreUnused (nativeView);
setEnv (env);
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceCreated (holder);
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->detachedFromWindow();
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceDestroyedNative,
void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
void AndroidGLCallbacks::dispatchDraw (JNIEnv*, jobject /*this*/, jlong host, jobject canvas)
{
ignoreUnused (nativeView);
setEnv (env);
reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceDestroyed (holder);
if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
nativeContext->dispatchDraw (canvas);
}
//==============================================================================


+ 2
- 2
modules/juce_opengl/opengl/juce_OpenGLContext.cpp View File

@@ -1218,7 +1218,7 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
EGLDisplay OpenGLContext::NativeContext::config;
void OpenGLContext::NativeContext::surfaceCreated (jobject holder)
void OpenGLContext::NativeContext::surfaceCreated (LocalRef<jobject> holder)
{
ignoreUnused (holder);
@@ -1235,7 +1235,7 @@ void OpenGLContext::NativeContext::surfaceCreated (jobject holder)
}
}
void OpenGLContext::NativeContext::surfaceDestroyed (jobject holder)
void OpenGLContext::NativeContext::surfaceDestroyed (LocalRef<jobject> holder)
{
ignoreUnused (holder);


+ 65
- 0
modules/juce_video/native/java/com/roli/juce/CameraCaptureSessionCaptureCallback.java View File

@@ -0,0 +1,65 @@
package com.roli.juce;

import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureResult;

public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
{
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);

CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
{
host = hostToUse;
preview = shouldBePreview;
}

@Override
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result)
{
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
}

@Override
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
{
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
}

@Override
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult)
{
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
}

@Override
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
{
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
}

@Override
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
{
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
}

@Override
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
long frameNumber)
{
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
}

private long host;
private boolean preview;
}

+ 53
- 0
modules/juce_video/native/java/com/roli/juce/CameraCaptureSessionStateCallback.java View File

@@ -0,0 +1,53 @@
package com.roli.juce;

import android.hardware.camera2.CameraCaptureSession;

public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
{
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);

private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);

private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);

private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);

private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);

CameraCaptureSessionStateCallback (long hostToUse)
{
host = hostToUse;
}

@Override
public void onActive (CameraCaptureSession session)
{
cameraCaptureSessionActive (host, session);
}

@Override
public void onClosed (CameraCaptureSession session)
{
cameraCaptureSessionClosed (host, session);
}

@Override
public void onConfigureFailed (CameraCaptureSession session)
{
cameraCaptureSessionConfigureFailed (host, session);
}

@Override
public void onConfigured (CameraCaptureSession session)
{
cameraCaptureSessionConfigured (host, session);
}

@Override
public void onReady (CameraCaptureSession session)
{
cameraCaptureSessionReady (host, session);
}

private long host;
}

+ 43
- 0
modules/juce_video/native/java/com/roli/juce/CameraDeviceStateCallback.java View File

@@ -0,0 +1,43 @@
package com.roli.juce;

import android.hardware.camera2.CameraDevice;

public class CameraDeviceStateCallback extends CameraDevice.StateCallback
{
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
private native void cameraDeviceStateOpened (long host, CameraDevice camera);

CameraDeviceStateCallback (long hostToUse)
{
host = hostToUse;
}

@Override
public void onClosed (CameraDevice camera)
{
cameraDeviceStateClosed (host, camera);
}

@Override
public void onDisconnected (CameraDevice camera)
{
cameraDeviceStateDisconnected (host, camera);
}

@Override
public void onError (CameraDevice camera, int error)
{
cameraDeviceStateError (host, camera, error);
}

@Override
public void onOpened (CameraDevice camera)
{
cameraDeviceStateOpened (host, camera);
}

private long host;
}


+ 24
- 0
modules/juce_video/native/java/com/roli/juce/JuceOrientationEventListener.java View File

@@ -0,0 +1,24 @@
package com.roli.juce;

import android.view.OrientationEventListener;
import android.content.Context;

public class JuceOrientationEventListener extends OrientationEventListener
{
private native void deviceOrientationChanged (long host, int orientation);

public JuceOrientationEventListener (long hostToUse, Context context, int rate)
{
super (context, rate);

host = hostToUse;
}

@Override
public void onOrientationChanged (int orientation)
{
deviceOrientationChanged (host, orientation);
}

private long host;
}

+ 53
- 0
modules/juce_video/native/java/com/roli/juce/MediaControllerCallback.java View File

@@ -0,0 +1,53 @@
package com.roli.juce;

import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.MediaMetadata;
import android.media.session.PlaybackState;

import java.util.List;

//==============================================================================
public class MediaControllerCallback extends MediaController.Callback
{
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
private native void mediaControllerSessionDestroyed (long host);

MediaControllerCallback (long hostToUse)
{
host = hostToUse;
}

@Override
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
{
mediaControllerAudioInfoChanged (host, info);
}

@Override
public void onMetadataChanged (MediaMetadata metadata)
{
mediaControllerMetadataChanged (host, metadata);
}

@Override
public void onPlaybackStateChanged (PlaybackState state)
{
mediaControllerPlaybackStateChanged (host, state);
}

@Override
public void onQueueChanged (List<MediaSession.QueueItem> queue)
{
}

@Override
public void onSessionDestroyed ()
{
mediaControllerSessionDestroyed (host);
}

private long host;
}

+ 78
- 0
modules/juce_video/native/java/com/roli/juce/MediaSessionCallback.java View File

@@ -0,0 +1,78 @@
package com.roli.juce;

import android.media.session.MediaSession;

import java.lang.String;

import android.os.Bundle;
import android.content.Intent;

import java.util.List;

//==============================================================================
public class MediaSessionCallback extends MediaSession.Callback
{
private native void mediaSessionPause (long host);
private native void mediaSessionPlay (long host);
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
private native void mediaSessionSeekTo (long host, long pos);
private native void mediaSessionStop (long host);

MediaSessionCallback (long hostToUse)
{
host = hostToUse;
}

@Override
public void onPause ()
{
mediaSessionPause (host);
}

@Override
public void onPlay ()
{
mediaSessionPlay (host);
}

@Override
public void onPlayFromMediaId (String mediaId, Bundle extras)
{
mediaSessionPlayFromMediaId (host, mediaId, extras);
}

@Override
public void onSeekTo (long pos)
{
mediaSessionSeekTo (host, pos);
}

@Override
public void onStop ()
{
mediaSessionStop (host);
}

@Override
public void onFastForward () {}

@Override
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
{
return true;
}

@Override
public void onRewind () {}

@Override
public void onSkipToNext () {}

@Override
public void onSkipToPrevious () {}

@Override
public void onSkipToQueueItem (long id) {}

private long host;
}

+ 37
- 0
modules/juce_video/native/java/com/roli/juce/SystemVolumeObserver.java View File

@@ -0,0 +1,37 @@
package com.roli.juce;

import android.database.ContentObserver;
import android.app.Activity;
import android.net.Uri;

//==============================================================================
public class SystemVolumeObserver extends ContentObserver
{
private native void mediaSessionSystemVolumeChanged (long host);

SystemVolumeObserver (Activity activityToUse, long hostToUse)
{
super (null);

activity = activityToUse;
host = hostToUse;
}

void setEnabled (boolean shouldBeEnabled)
{
if (shouldBeEnabled)
activity.getApplicationContext ().getContentResolver ().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
else
activity.getApplicationContext ().getContentResolver ().unregisterContentObserver (this);
}

@Override
public void onChange (boolean selfChange, Uri uri)
{
if (uri.toString ().startsWith ("content://settings/system/volume_music"))
mediaSessionSystemVolumeChanged (host);
}

private Activity activity;
private long host;
}

+ 403
- 437
modules/juce_video/native/juce_android_CameraDevice.h
File diff suppressed because it is too large
View File


+ 336
- 399
modules/juce_video/native/juce_android_Video.h
File diff suppressed because it is too large
View File


+ 0
- 18
modules/juce_video/playback/juce_VideoComponent.h View File

@@ -177,24 +177,6 @@ private:
void resized() override;
void timerCallback() override;
#if JUCE_ANDROID
friend void juce_surfaceChangedNativeVideo (int64, void*);
friend void juce_surfaceDestroyedNativeVideo (int64, void*);
friend void juce_mediaSessionPause (int64);
friend void juce_mediaSessionPlay (int64);
friend void juce_mediaSessionPlayFromMediaId (int64, void*, void*);
friend void juce_mediaSessionSeekTo (int64, int64);
friend void juce_mediaSessionStop (int64);
friend void juce_mediaControllerAudioInfoChanged (int64, void*);
friend void juce_mediaControllerMetadataChanged (int64, void*);
friend void juce_mediaControllerPlaybackStateChanged (int64, void*);
friend void juce_mediaControllerSessionDestroyed (int64);
friend void juce_mediaSessionSystemVolumeChanged (int64);
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoComponent)
};


Loading…
Cancel
Save