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
| @@ -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. | |||
| @@ -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 { | |||
| } | |||
| } | |||
| @@ -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> | |||
| @@ -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) | |||
| @@ -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 { | |||
| } | |||
| } | |||
| @@ -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"/> | |||
| @@ -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) | |||
| @@ -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 { | |||
| } | |||
| } | |||
| @@ -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"/> | |||
| @@ -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) | |||
| @@ -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 { | |||
| } | |||
| } | |||
| @@ -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"/> | |||
| @@ -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"); | |||
| } | |||
| @@ -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); | |||
| @@ -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) | |||
| @@ -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; | |||
| @@ -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")); | |||
| @@ -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) | |||
| @@ -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; | |||
| } | |||
| @@ -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" | |||
| @@ -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$$ | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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$$ | |||
| @@ -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$$ | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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()); | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| @@ -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 {}; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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, | |||
| @@ -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 | |||
| @@ -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;")))); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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: | |||
| //============================================================================== | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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) | |||
| }; | |||
| @@ -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, | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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$$ | |||
| } | |||
| @@ -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 | |||
| @@ -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() | |||
| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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 (); | |||
| } | |||
| } | |||
| @@ -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)); | |||
| } | |||
| } | |||
| } | |||
| @@ -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)); | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| //============================================================================== | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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) | |||
| }; | |||