mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
Android: fix PushNotifications that got broken by Android low level code rework.
This commit is contained in:
parent
1c033e410b
commit
7c4601473a
22 changed files with 356 additions and 123 deletions
|
|
@ -1,39 +1,37 @@
|
||||||
{
|
{
|
||||||
"project_info": {
|
"project_info": {
|
||||||
"project_number": "3137221487",
|
"project_number": "50526851168",
|
||||||
"firebase_url": "https://pushnotificationsdemo-1c714.firebaseio.com",
|
"firebase_url": "https://pushnotificationsdemorunner.firebaseio.com",
|
||||||
"project_id": "pushnotificationsdemo-1c714",
|
"project_id": "pushnotificationsdemorunner",
|
||||||
"storage_bucket": "pushnotificationsdemo-1c714.appspot.com"
|
"storage_bucket": "pushnotificationsdemorunner.appspot.com"
|
||||||
},
|
},
|
||||||
"client": [
|
"client": [
|
||||||
{
|
{
|
||||||
"client_info": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:3137221487:android:8fdcd861a33b035c",
|
"mobilesdk_app_id": "1:50526851168:android:6fa3f0d4b79f1940",
|
||||||
"android_client_info": {
|
"android_client_info": {
|
||||||
"package_name": "com.juce.pushnotificationsdemo"
|
"package_name": "com.juce.demorunner"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"oauth_client": [
|
"oauth_client": [
|
||||||
{
|
{
|
||||||
"client_id": "3137221487-uftk61ukltbi07dmejslgt0d6qnml0oo.apps.googleusercontent.com",
|
"client_id": "50526851168-vgn4rv0vimpc8kdm7ecmb3g95t1et0t5.apps.googleusercontent.com",
|
||||||
"client_type": 3
|
"client_type": 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_key": [
|
"api_key": [
|
||||||
{
|
{
|
||||||
"current_key": "AIzaSyDPpqphjiEEYI3sJGptrebN5Z52GkOG4Wo"
|
"current_key": "AIzaSyAMwLOFACFo7_SHm9iiVhoa0zCjFyMsgFc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": {
|
"services": {
|
||||||
"analytics_service": {
|
|
||||||
"status": 1
|
|
||||||
},
|
|
||||||
"appinvite_service": {
|
"appinvite_service": {
|
||||||
"status": 1,
|
"other_platform_oauth_client": [
|
||||||
"other_platform_oauth_client": []
|
{
|
||||||
},
|
"client_id": "50526851168-vgn4rv0vimpc8kdm7ecmb3g95t1et0t5.apps.googleusercontent.com",
|
||||||
"ads_service": {
|
"client_type": 3
|
||||||
"status": 2
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni")
|
||||||
add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c")
|
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")
|
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_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=5.4.3" "-DJUCE_APP_VERSION_HEX=0x50403")
|
add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_PUSH_NOTIFICATIONS_ACTIVITY=\"com/roli/juce/JuceActivity\"" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=5.4.3" "-DJUCE_APP_VERSION_HEX=0x50403")
|
||||||
|
|
||||||
include_directories( AFTER
|
include_directories( AFTER
|
||||||
"../../../JuceLibraryCode"
|
"../../../JuceLibraryCode"
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@ android {
|
||||||
main.java.srcDirs +=
|
main.java.srcDirs +=
|
||||||
["../../../../../modules/juce_core/native/javacore/init",
|
["../../../../../modules/juce_core/native/javacore/init",
|
||||||
"../../../../../modules/juce_core/native/javacore/app",
|
"../../../../../modules/juce_core/native/javacore/app",
|
||||||
"../../../../../modules/juce_gui_basics/native/javacore/app"]
|
"../../../../../modules/juce_gui_basics/native/javacore/app",
|
||||||
|
"../../../../../modules/juce_gui_basics/native/javaopt/app"]
|
||||||
|
|
||||||
main.res.srcDirs +=
|
main.res.srcDirs +=
|
||||||
[]
|
[]
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
||||||
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
|
<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"
|
<activity android:name="com.roli.juce.JuceActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,37 @@
|
||||||
{
|
{
|
||||||
"project_info": {
|
"project_info": {
|
||||||
"project_number": "3137221487",
|
"project_number": "50526851168",
|
||||||
"firebase_url": "https://pushnotificationsdemo-1c714.firebaseio.com",
|
"firebase_url": "https://pushnotificationsdemorunner.firebaseio.com",
|
||||||
"project_id": "pushnotificationsdemo-1c714",
|
"project_id": "pushnotificationsdemorunner",
|
||||||
"storage_bucket": "pushnotificationsdemo-1c714.appspot.com"
|
"storage_bucket": "pushnotificationsdemorunner.appspot.com"
|
||||||
},
|
},
|
||||||
"client": [
|
"client": [
|
||||||
{
|
{
|
||||||
"client_info": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:3137221487:android:8fdcd861a33b035c",
|
"mobilesdk_app_id": "1:50526851168:android:6fa3f0d4b79f1940",
|
||||||
"android_client_info": {
|
"android_client_info": {
|
||||||
"package_name": "com.juce.pushnotificationsdemo"
|
"package_name": "com.juce.demorunner"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"oauth_client": [
|
"oauth_client": [
|
||||||
{
|
{
|
||||||
"client_id": "3137221487-uftk61ukltbi07dmejslgt0d6qnml0oo.apps.googleusercontent.com",
|
"client_id": "50526851168-vgn4rv0vimpc8kdm7ecmb3g95t1et0t5.apps.googleusercontent.com",
|
||||||
"client_type": 3
|
"client_type": 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_key": [
|
"api_key": [
|
||||||
{
|
{
|
||||||
"current_key": "AIzaSyDPpqphjiEEYI3sJGptrebN5Z52GkOG4Wo"
|
"current_key": "AIzaSyAMwLOFACFo7_SHm9iiVhoa0zCjFyMsgFc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": {
|
"services": {
|
||||||
"analytics_service": {
|
|
||||||
"status": 1
|
|
||||||
},
|
|
||||||
"appinvite_service": {
|
"appinvite_service": {
|
||||||
"status": 1,
|
"other_platform_oauth_client": [
|
||||||
"other_platform_oauth_client": []
|
{
|
||||||
},
|
"client_id": "50526851168-vgn4rv0vimpc8kdm7ecmb3g95t1et0t5.apps.googleusercontent.com",
|
||||||
"ads_service": {
|
"client_type": 3
|
||||||
"status": 2
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -51,6 +51,74 @@
|
||||||
|
|
||||||
#include "../Assets/DemoUtilities.h"
|
#include "../Assets/DemoUtilities.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
To finish the setup of this demo, do the following:
|
||||||
|
|
||||||
|
1. Download google_services.json from your Firebase project.
|
||||||
|
2. Update "Remote Notifications Config File" path in Android exporter (this can be different for debug and release)
|
||||||
|
to point to that json file.
|
||||||
|
3. Add image and sound resources by adding the following to "Extra Android Raw Resources" in Projucer:
|
||||||
|
|
||||||
|
../../Assets/Notifications/images/ic_stat_name.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name2.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name3.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name4.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name5.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name6.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name7.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name8.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name9.png
|
||||||
|
../../Assets/Notifications/images/ic_stat_name10.png
|
||||||
|
../../Assets/Notifications/sounds/demonstrative.mp3
|
||||||
|
../../Assets/Notifications/sounds/isntit.mp3
|
||||||
|
../../Assets/Notifications/sounds/jinglebellssms.mp3
|
||||||
|
../../Assets/Notifications/sounds/served.mp3
|
||||||
|
../../Assets/Notifications/sounds/solemn.mp3
|
||||||
|
|
||||||
|
4. Set "Remote Notifications" to enabled in Projucer Android exporter.
|
||||||
|
|
||||||
|
To verify that remote notifications are configured properly, go to Remote tab in the demo and press "GetDeviceToken"
|
||||||
|
button, a dialog with your token (also printed to console in debug build) should show up.
|
||||||
|
|
||||||
|
|
||||||
|
The following steps are only necessary if you have a custom activity defined:
|
||||||
|
|
||||||
|
5. Ensure that its launchMode is set to "singleTop" or "singleTask" in Android manifest. This is the default behaviour
|
||||||
|
in JUCE so you only need to do it if you have custom Android manifest content. You can do it from Projucer by
|
||||||
|
ensuring that "Custom Manifest XML Content" contains:
|
||||||
|
|
||||||
|
<manifest>
|
||||||
|
<application>
|
||||||
|
<activity android:launchMode="singleTask">
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
|
|
||||||
|
6. Ensure that you override onNewIntent() function in the same way as it is done in JuceActivity.java:
|
||||||
|
|
||||||
|
package com.roli.juce;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class JuceActivity extends Activity
|
||||||
|
{
|
||||||
|
//==============================================================================
|
||||||
|
private native void appNewIntent (Intent intent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent (Intent intent)
|
||||||
|
{
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
|
||||||
|
appNewIntent (intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class PushNotificationsDemo : public Component,
|
class PushNotificationsDemo : public Component,
|
||||||
private ChangeListener,
|
private ChangeListener,
|
||||||
|
|
@ -101,11 +169,11 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sendButton.onClick = [this] { sendLocalNotification(); };
|
sendButton.onClick = [this] { sendLocalNotification(); };
|
||||||
auxActionsView.getDeliveredNotificationsButton .onClick = [this]
|
auxActionsView.getDeliveredNotificationsButton .onClick = []
|
||||||
{ PushNotifications::getInstance()->getDeliveredNotifications(); };
|
{ PushNotifications::getInstance()->getDeliveredNotifications(); };
|
||||||
auxActionsView.removeDeliveredNotifWithIdButton.onClick = [this]
|
auxActionsView.removeDeliveredNotifWithIdButton.onClick = [this]
|
||||||
{ PushNotifications::getInstance()->removeDeliveredNotification (auxActionsView.deliveredNotifIdentifier.getText()); };
|
{ PushNotifications::getInstance()->removeDeliveredNotification (auxActionsView.deliveredNotifIdentifier.getText()); };
|
||||||
auxActionsView.removeAllDeliveredNotifsButton .onClick = [this]
|
auxActionsView.removeAllDeliveredNotifsButton .onClick = []
|
||||||
{ PushNotifications::getInstance()->removeAllDeliveredNotifications(); };
|
{ PushNotifications::getInstance()->removeAllDeliveredNotifications(); };
|
||||||
#if JUCE_IOS || JUCE_MAC
|
#if JUCE_IOS || JUCE_MAC
|
||||||
auxActionsView.getPendingNotificationsButton .onClick = [this]
|
auxActionsView.getPendingNotificationsButton .onClick = [this]
|
||||||
|
|
@ -116,7 +184,7 @@ public:
|
||||||
{ PushNotifications::getInstance()->removeAllPendingLocalNotifications(); };
|
{ PushNotifications::getInstance()->removeAllPendingLocalNotifications(); };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
remoteView.getDeviceTokenButton.onClick = [this]
|
remoteView.getDeviceTokenButton.onClick = []
|
||||||
{
|
{
|
||||||
String token = PushNotifications::getInstance()->getDeviceToken();
|
String token = PushNotifications::getInstance()->getDeviceToken();
|
||||||
|
|
||||||
|
|
@ -129,7 +197,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
#if JUCE_ANDROID
|
||||||
remoteView.sendRemoteMessageButton.onClick = [this]
|
remoteView.sendRemoteMessageButton.onClick = []
|
||||||
{
|
{
|
||||||
StringPairArray data;
|
StringPairArray data;
|
||||||
data.set ("key1", "value1");
|
data.set ("key1", "value1");
|
||||||
|
|
@ -144,9 +212,9 @@ public:
|
||||||
data);
|
data);
|
||||||
};
|
};
|
||||||
|
|
||||||
remoteView.subscribeToSportsButton .onClick = [this]
|
remoteView.subscribeToSportsButton .onClick = []
|
||||||
{ PushNotifications::getInstance()->subscribeToTopic ("sports"); };
|
{ PushNotifications::getInstance()->subscribeToTopic ("sports"); };
|
||||||
remoteView.unsubscribeFromSportsButton.onClick = [this]
|
remoteView.unsubscribeFromSportsButton.onClick = []
|
||||||
{ PushNotifications::getInstance()->unsubscribeFromTopic ("sports"); };
|
{ PushNotifications::getInstance()->unsubscribeFromTopic ("sports"); };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -164,11 +232,31 @@ public:
|
||||||
PushNotifications::ChannelGroup cg { "demoGroup", "demo group" };
|
PushNotifications::ChannelGroup cg { "demoGroup", "demo group" };
|
||||||
PushNotifications::getInstance()->setupChannels ({ { cg } }, getAndroidChannels());
|
PushNotifications::getInstance()->setupChannels ({ { cg } }, getAndroidChannels());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
|
setPortraitOrientationEnabled (true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~PushNotificationsDemo()
|
~PushNotificationsDemo()
|
||||||
{
|
{
|
||||||
PushNotifications::getInstance()->removeListener (this);
|
PushNotifications::getInstance()->removeListener (this);
|
||||||
|
|
||||||
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
|
setPortraitOrientationEnabled (false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPortraitOrientationEnabled (bool shouldBeEnabled)
|
||||||
|
{
|
||||||
|
auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
|
||||||
|
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
allowedOrientations |= Desktop::upright;
|
||||||
|
else
|
||||||
|
allowedOrientations &= ~Desktop::upright;
|
||||||
|
|
||||||
|
Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void paint (Graphics& g) override
|
void paint (Graphics& g) override
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni")
|
||||||
add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c")
|
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")
|
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_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_PUSH_NOTIFICATIONS_ACTIVITY=\"com/roli/juce/JuceActivity\"" "-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
|
include_directories( AFTER
|
||||||
"../../../JuceLibraryCode"
|
"../../../JuceLibraryCode"
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ android {
|
||||||
main.java.srcDirs +=
|
main.java.srcDirs +=
|
||||||
["../../../../../modules/juce_core/native/javacore/init",
|
["../../../../../modules/juce_core/native/javacore/init",
|
||||||
"../../../../../modules/juce_core/native/javacore/app",
|
"../../../../../modules/juce_core/native/javacore/app",
|
||||||
"../../../../../modules/juce_gui_basics/native/javacore/app"]
|
"../../../../../modules/juce_gui_basics/native/javacore/app",
|
||||||
|
"../../../../../modules/juce_gui_basics/native/javaopt/app"]
|
||||||
|
|
||||||
main.res.srcDirs +=
|
main.res.srcDirs +=
|
||||||
[]
|
[]
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:hardwareAccelerated="false">
|
<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"
|
<activity android:name="com.roli.juce.JuceActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<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")
|
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")
|
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_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_PUSH_NOTIFICATIONS_ACTIVITY=\"com/roli/juce/JuceActivity\"" "-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
|
include_directories( AFTER
|
||||||
"../../../../../modules/juce_audio_processors/format_types/VST3_SDK"
|
"../../../../../modules/juce_audio_processors/format_types/VST3_SDK"
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@ android {
|
||||||
main.java.srcDirs +=
|
main.java.srcDirs +=
|
||||||
["../../../../../modules/juce_core/native/javacore/init",
|
["../../../../../modules/juce_core/native/javacore/init",
|
||||||
"../../../../../modules/juce_core/native/javacore/app",
|
"../../../../../modules/juce_core/native/javacore/app",
|
||||||
"../../../../../modules/juce_gui_basics/native/javacore/app"]
|
"../../../../../modules/juce_gui_basics/native/javacore/app",
|
||||||
|
"../../../../../modules/juce_gui_basics/native/javaopt/app"]
|
||||||
|
|
||||||
main.res.srcDirs +=
|
main.res.srcDirs +=
|
||||||
[]
|
[]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
||||||
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
|
<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"
|
<activity android:name="com.roli.juce.JuceActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<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")
|
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")
|
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=16" "-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=16" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_PUSH_NOTIFICATIONS_ACTIVITY=\"com/roli/juce/JuceActivity\"" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000")
|
||||||
|
|
||||||
include_directories( AFTER
|
include_directories( AFTER
|
||||||
"../../../JuceLibraryCode"
|
"../../../JuceLibraryCode"
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@ android {
|
||||||
main.java.srcDirs +=
|
main.java.srcDirs +=
|
||||||
["../../../../../modules/juce_core/native/javacore/init",
|
["../../../../../modules/juce_core/native/javacore/init",
|
||||||
"../../../../../modules/juce_core/native/javacore/app",
|
"../../../../../modules/juce_core/native/javacore/app",
|
||||||
"../../../../../modules/juce_gui_basics/native/javacore/app"]
|
"../../../../../modules/juce_gui_basics/native/javacore/app",
|
||||||
|
"../../../../../modules/juce_gui_basics/native/javaopt/app"]
|
||||||
|
|
||||||
main.res.srcDirs +=
|
main.res.srcDirs +=
|
||||||
[]
|
[]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||||
<application android:label="@string/app_name" android:name="com.roli.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
|
<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"
|
<activity android:name="com.roli.juce.JuceActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ public:
|
||||||
|
|
||||||
static const char* getName() { return "Android"; }
|
static const char* getName() { return "Android"; }
|
||||||
static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; }
|
static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; }
|
||||||
|
static const char* getDefaultActivityClass() { return "com.roli.juce.JuceActivity"; }
|
||||||
|
static const char* getDefaultApplicationClass() { return "com.roli.juce.JuceApp"; }
|
||||||
|
|
||||||
static AndroidProjectExporter* createForSettings (Project& project, const ValueTree& settings)
|
static AndroidProjectExporter* createForSettings (Project& project, const ValueTree& settings)
|
||||||
{
|
{
|
||||||
|
|
@ -98,7 +100,7 @@ public:
|
||||||
androidCustomActivityClass, androidCustomApplicationClass, androidManifestCustomXmlElements, androidVersionCode,
|
androidCustomActivityClass, androidCustomApplicationClass, androidManifestCustomXmlElements, androidVersionCode,
|
||||||
androidMinimumSDK, androidTargetSDK, androidTheme, androidSharedLibraries, androidStaticLibraries, androidExtraAssetsFolder,
|
androidMinimumSDK, androidTargetSDK, androidTheme, androidSharedLibraries, androidStaticLibraries, androidExtraAssetsFolder,
|
||||||
androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidCameraNeeded, androidBluetoothNeeded, androidExternalReadPermission,
|
androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidCameraNeeded, androidBluetoothNeeded, androidExternalReadPermission,
|
||||||
androidExternalWritePermission, androidInAppBillingPermission, androidVibratePermission,androidOtherPermissions,
|
androidExternalWritePermission, androidInAppBillingPermission, androidVibratePermission, androidOtherPermissions,
|
||||||
androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore,
|
androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore,
|
||||||
androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, androidPluginVersion;
|
androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, androidPluginVersion;
|
||||||
|
|
||||||
|
|
@ -111,8 +113,8 @@ public:
|
||||||
androidRepositories (settings, Ids::androidRepositories, getUndoManager()),
|
androidRepositories (settings, Ids::androidRepositories, getUndoManager()),
|
||||||
androidDependencies (settings, Ids::androidDependencies, getUndoManager()),
|
androidDependencies (settings, Ids::androidDependencies, getUndoManager()),
|
||||||
androidScreenOrientation (settings, Ids::androidScreenOrientation, getUndoManager(), "unspecified"),
|
androidScreenOrientation (settings, Ids::androidScreenOrientation, getUndoManager(), "unspecified"),
|
||||||
androidCustomActivityClass (settings, Ids::androidCustomActivityClass, getUndoManager()),
|
androidCustomActivityClass (settings, Ids::androidCustomActivityClass, getUndoManager(), getDefaultActivityClass()),
|
||||||
androidCustomApplicationClass (settings, Ids::androidCustomApplicationClass, getUndoManager(), "com.roli.juce.JuceApp"),
|
androidCustomApplicationClass (settings, Ids::androidCustomApplicationClass, getUndoManager(), getDefaultApplicationClass()),
|
||||||
androidManifestCustomXmlElements (settings, Ids::androidManifestCustomXmlElements, getUndoManager()),
|
androidManifestCustomXmlElements (settings, Ids::androidManifestCustomXmlElements, getUndoManager()),
|
||||||
androidVersionCode (settings, Ids::androidVersionCode, getUndoManager(), "1"),
|
androidVersionCode (settings, Ids::androidVersionCode, getUndoManager(), "1"),
|
||||||
androidMinimumSDK (settings, Ids::androidMinimumSDK, getUndoManager(), "16"),
|
androidMinimumSDK (settings, Ids::androidMinimumSDK, getUndoManager(), "16"),
|
||||||
|
|
@ -570,7 +572,7 @@ private:
|
||||||
mo << " classpath 'com.android.tools.build:gradle:" << androidPluginVersion.get().toString() << "'" << newLine;
|
mo << " classpath 'com.android.tools.build:gradle:" << androidPluginVersion.get().toString() << "'" << newLine;
|
||||||
|
|
||||||
if (androidEnableRemoteNotifications.get())
|
if (androidEnableRemoteNotifications.get())
|
||||||
mo << " classpath 'com.google.gms:google-services:3.1.0'" << newLine;
|
mo << " classpath 'com.google.gms:google-services:4.0.1'" << newLine;
|
||||||
|
|
||||||
mo << " }" << newLine;
|
mo << " }" << newLine;
|
||||||
mo << "}" << newLine;
|
mo << "}" << newLine;
|
||||||
|
|
@ -814,8 +816,8 @@ private:
|
||||||
|
|
||||||
if (androidEnableRemoteNotifications.get())
|
if (androidEnableRemoteNotifications.get())
|
||||||
{
|
{
|
||||||
mo << " 'com.google.firebase:firebase-core:11.4.0'" << newLine;
|
mo << " implementation 'com.google.firebase:firebase-core:16.0.1'" << newLine;
|
||||||
mo << " compile 'com.google.firebase:firebase-messaging:11.4.0'" << newLine;
|
mo << " implementation 'com.google.firebase:firebase-messaging:17.6.0'" << newLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
mo << " }" << newLine;
|
mo << " }" << newLine;
|
||||||
|
|
@ -834,17 +836,32 @@ private:
|
||||||
return mo.toString();
|
return mo.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addModuleJavaFolderToSourceSet(StringArray& javaSourceSets, const File& javacore) const
|
void addModuleJavaFolderToSourceSet(StringArray& javaSourceSets, const File& source) const
|
||||||
{
|
{
|
||||||
if (javacore.isDirectory())
|
if (source.isDirectory())
|
||||||
{
|
{
|
||||||
auto appFolder = getTargetFolder().getChildFile ("app");
|
auto appFolder = getTargetFolder().getChildFile ("app");
|
||||||
|
|
||||||
RelativePath relativePath (javacore, appFolder, RelativePath::buildTargetFolder);
|
RelativePath relativePath (source, appFolder, RelativePath::buildTargetFolder);
|
||||||
javaSourceSets.add (relativePath.toUnixStyle());
|
javaSourceSets.add (relativePath.toUnixStyle());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addOptJavaFolderToSourceSetsForModule (StringArray& javaSourceSets,
|
||||||
|
const OwnedArray<LibraryModule>& modules,
|
||||||
|
const String& moduleID) const
|
||||||
|
{
|
||||||
|
for (auto& m : modules)
|
||||||
|
{
|
||||||
|
if (m->getID() == moduleID)
|
||||||
|
{
|
||||||
|
auto javaFolder = m->getFolder().getChildFile ("native").getChildFile ("javaopt");
|
||||||
|
addModuleJavaFolderToSourceSet (javaSourceSets, javaFolder.getChildFile("app"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String getAndroidJavaSourceSets (const OwnedArray<LibraryModule>& modules) const
|
String getAndroidJavaSourceSets (const OwnedArray<LibraryModule>& modules) const
|
||||||
{
|
{
|
||||||
auto javaSourceSets = getSourceSetArrayFor (androidAdditionalJavaFolders.get().toString());
|
auto javaSourceSets = getSourceSetArrayFor (androidAdditionalJavaFolders.get().toString());
|
||||||
|
|
@ -860,6 +877,12 @@ private:
|
||||||
addModuleJavaFolderToSourceSet (javaSourceSets, javaFolder.getChildFile("app"));
|
addModuleJavaFolderToSourceSet (javaSourceSets, javaFolder.getChildFile("app"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (androidCustomActivityClass.get() == getDefaultActivityClass())
|
||||||
|
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_gui_basics");
|
||||||
|
|
||||||
|
if (androidEnableRemoteNotifications.get())
|
||||||
|
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_gui_extra");
|
||||||
|
|
||||||
MemoryOutputStream mo;
|
MemoryOutputStream mo;
|
||||||
mo.setNewLineString ("\n");
|
mo.setNewLineString ("\n");
|
||||||
|
|
||||||
|
|
@ -974,7 +997,9 @@ private:
|
||||||
|
|
||||||
props.add (new TextPropertyComponent (androidCustomActivityClass, "Custom Android Activity", 256, false),
|
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 "
|
"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.");
|
"should be used instead of default com.roli.juce.JuceActivity. If you specify a custom Activity "
|
||||||
|
"then you should implement onNewIntent() function like the one in com.roli.juce.JuceActivity, if "
|
||||||
|
"you wish to be able to handle push notification events.");
|
||||||
|
|
||||||
props.add (new TextPropertyComponent (androidCustomApplicationClass, "Custom Android Application", 256, false),
|
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 "
|
"If not empty, specifies the Android Application class name stored in the app's manifest which "
|
||||||
|
|
@ -1135,19 +1160,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getActivityClass() const
|
String getActivityClass() const { return androidCustomActivityClass.get(); }
|
||||||
{
|
String getApplicationClass() const { return androidCustomApplicationClass.get(); }
|
||||||
auto customActivityClass = androidCustomActivityClass.get().toString();
|
|
||||||
|
|
||||||
return (customActivityClass.isEmpty()) ? "android.app.Activity" : customActivityClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getApplicationClass() const
|
|
||||||
{
|
|
||||||
auto customApplicationClass = androidCustomApplicationClass.get().toString();
|
|
||||||
|
|
||||||
return (customApplicationClass.isEmpty()) ? "com.roli.juce.JuceApp" : customApplicationClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getJNIActivityClassName() const
|
String getJNIActivityClassName() const
|
||||||
{
|
{
|
||||||
|
|
@ -1374,6 +1388,7 @@ private:
|
||||||
defines.set ("JUCE_ANDROID", "1");
|
defines.set ("JUCE_ANDROID", "1");
|
||||||
defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get());
|
defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get());
|
||||||
defines.set ("JUCE_PUSH_NOTIFICATIONS", "1");
|
defines.set ("JUCE_PUSH_NOTIFICATIONS", "1");
|
||||||
|
defines.set ("JUCE_PUSH_NOTIFICATIONS_ACTIVITY", String::formatted("\"%s\"", getJNIActivityClassName().toUTF8()));
|
||||||
|
|
||||||
if (androidInAppBillingPermission.get())
|
if (androidInAppBillingPermission.get())
|
||||||
defines.set ("JUCE_IN_APP_PURCHASES", "1");
|
defines.set ("JUCE_IN_APP_PURCHASES", "1");
|
||||||
|
|
@ -1381,6 +1396,12 @@ private:
|
||||||
if (supportsGLv3())
|
if (supportsGLv3())
|
||||||
defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1");
|
defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1");
|
||||||
|
|
||||||
|
if (androidEnableRemoteNotifications.get())
|
||||||
|
{
|
||||||
|
defines.set ("JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME", "com_roli_juce_JuceFirebaseInstanceIdService");
|
||||||
|
defines.set ("JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME", "com_roli_juce_JuceFirebaseMessagingService");
|
||||||
|
}
|
||||||
|
|
||||||
return defines;
|
return defines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1683,12 +1704,12 @@ private:
|
||||||
if (androidEnableRemoteNotifications.get())
|
if (androidEnableRemoteNotifications.get())
|
||||||
{
|
{
|
||||||
auto* service = application.createNewChildElement ("service");
|
auto* service = application.createNewChildElement ("service");
|
||||||
service->setAttribute ("android:name", ".JuceFirebaseMessagingService");
|
service->setAttribute ("android:name", "com.roli.juce.JuceFirebaseMessagingService");
|
||||||
auto* intentFilter = service->createNewChildElement ("intent-filter");
|
auto* intentFilter = service->createNewChildElement ("intent-filter");
|
||||||
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.MESSAGING_EVENT");
|
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.MESSAGING_EVENT");
|
||||||
|
|
||||||
service = application.createNewChildElement ("service");
|
service = application.createNewChildElement ("service");
|
||||||
service->setAttribute ("android:name", ".JuceFirebaseInstanceIdService");
|
service->setAttribute ("android:name", "com.roli.juce.JuceFirebaseInstanceIdService");
|
||||||
intentFilter = service->createNewChildElement ("intent-filter");
|
intentFilter = service->createNewChildElement ("intent-filter");
|
||||||
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.INSTANCE_ID_EVENT");
|
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.INSTANCE_ID_EVENT");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.roli.juce;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class JuceActivity extends Activity
|
||||||
|
{
|
||||||
|
//==============================================================================
|
||||||
|
private native void appNewIntent (Intent intent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent (Intent intent)
|
||||||
|
{
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
|
||||||
|
appNewIntent (intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -700,11 +700,9 @@ private:
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
#if JUCE_ANDROID
|
||||||
friend bool juce_handleNotificationIntent (void*);
|
friend bool juce_handleNotificationIntent (void*);
|
||||||
friend void juce_firebaseDeviceNotificationsTokenRefreshed (void*);
|
|
||||||
friend void juce_firebaseRemoteNotificationReceived (void*);
|
friend struct JuceFirebaseInstanceIdService;
|
||||||
friend void juce_firebaseRemoteMessagesDeleted();
|
friend struct JuceFirebaseMessagingService;
|
||||||
friend void juce_firebaseRemoteMessageSent (void*);
|
|
||||||
friend void juce_firebaseRemoteMessageSendError (void*, void*);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JUCE_PUSH_NOTIFICATIONS
|
#if JUCE_PUSH_NOTIFICATIONS
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.roli.juce;
|
||||||
|
|
||||||
|
import com.google.firebase.iid.*;
|
||||||
|
|
||||||
|
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService
|
||||||
|
{
|
||||||
|
private native void firebaseInstanceIdTokenRefreshed (String token);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTokenRefresh()
|
||||||
|
{
|
||||||
|
String token = FirebaseInstanceId.getInstance().getToken();
|
||||||
|
|
||||||
|
firebaseInstanceIdTokenRefreshed (token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.roli.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -203,8 +203,8 @@ DECLARE_JNI_CLASS_WITH_MIN_SDK (RemoteInputBuilder, "android/app/RemoteInput$Bui
|
||||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||||
STATICMETHOD (getInstance, "getInstance", "()Lcom/google/firebase/messaging/FirebaseMessaging;") \
|
STATICMETHOD (getInstance, "getInstance", "()Lcom/google/firebase/messaging/FirebaseMessaging;") \
|
||||||
METHOD (send, "send", "(Lcom/google/firebase/messaging/RemoteMessage;)V") \
|
METHOD (send, "send", "(Lcom/google/firebase/messaging/RemoteMessage;)V") \
|
||||||
METHOD (subscribeToTopic, "subscribeToTopic", "(Ljava/lang/String;)V") \
|
METHOD (subscribeToTopic, "subscribeToTopic", "(Ljava/lang/String;)Lcom/google/android/gms/tasks/Task;") \
|
||||||
METHOD (unsubscribeFromTopic, "unsubscribeFromTopic", "(Ljava/lang/String;)V") \
|
METHOD (unsubscribeFromTopic, "unsubscribeFromTopic", "(Ljava/lang/String;)Lcom/google/android/gms/tasks/Task;") \
|
||||||
|
|
||||||
DECLARE_JNI_CLASS (FirebaseMessaging, "com/google/firebase/messaging/FirebaseMessaging")
|
DECLARE_JNI_CLASS (FirebaseMessaging, "com/google/firebase/messaging/FirebaseMessaging")
|
||||||
#undef JNI_CLASS_MEMBERS
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
|
@ -349,7 +349,7 @@ struct PushNotifications::Pimpl
|
||||||
void notifyListenersAboutLocalNotification (const LocalRef<jobject>& intent)
|
void notifyListenersAboutLocalNotification (const LocalRef<jobject>& intent)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, AndroidIntent.getExtras));
|
auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, AndroidIntent.getExtras));
|
||||||
|
|
||||||
|
|
@ -461,6 +461,7 @@ struct PushNotifications::Pimpl
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
void subscribeToTopic (const String& topic)
|
void subscribeToTopic (const String& topic)
|
||||||
{
|
{
|
||||||
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
||||||
|
|
@ -469,7 +470,7 @@ struct PushNotifications::Pimpl
|
||||||
auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
|
auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
|
||||||
FirebaseMessaging.getInstance));
|
FirebaseMessaging.getInstance));
|
||||||
|
|
||||||
env->CallVoidMethod (firebaseMessaging, FirebaseMessaging.subscribeToTopic, javaString (topic).get());
|
env->CallObjectMethod (firebaseMessaging, FirebaseMessaging.subscribeToTopic, javaString (topic).get());
|
||||||
#else
|
#else
|
||||||
ignoreUnused (topic);
|
ignoreUnused (topic);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -483,7 +484,7 @@ struct PushNotifications::Pimpl
|
||||||
auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
|
auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
|
||||||
FirebaseMessaging.getInstance));
|
FirebaseMessaging.getInstance));
|
||||||
|
|
||||||
env->CallVoidMethod (firebaseMessaging, FirebaseMessaging.unsubscribeFromTopic, javaString (topic).get());
|
env->CallObjectMethod (firebaseMessaging, FirebaseMessaging.unsubscribeFromTopic, javaString (topic).get());
|
||||||
#else
|
#else
|
||||||
ignoreUnused (topic);
|
ignoreUnused (topic);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -545,7 +546,7 @@ struct PushNotifications::Pimpl
|
||||||
void notifyListenersAboutRemoteNotificationFromService (const LocalRef<jobject>& remoteNotification)
|
void notifyListenersAboutRemoteNotificationFromService (const LocalRef<jobject>& remoteNotification)
|
||||||
{
|
{
|
||||||
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
||||||
GlobalRef rn (remoteNotification.get());
|
GlobalRef rn (remoteNotification);
|
||||||
|
|
||||||
MessageManager::callAsync ([this, rn]
|
MessageManager::callAsync ([this, rn]
|
||||||
{
|
{
|
||||||
|
|
@ -570,7 +571,7 @@ struct PushNotifications::Pimpl
|
||||||
void notifyListenersAboutUpstreamMessageSent (const LocalRef<jstring>& messageId)
|
void notifyListenersAboutUpstreamMessageSent (const LocalRef<jstring>& messageId)
|
||||||
{
|
{
|
||||||
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
||||||
GlobalRef mid (messageId);
|
GlobalRef mid (LocalRef<jobject>(messageId.get()));
|
||||||
|
|
||||||
MessageManager::callAsync ([this, mid]
|
MessageManager::callAsync ([this, mid]
|
||||||
{
|
{
|
||||||
|
|
@ -586,7 +587,7 @@ struct PushNotifications::Pimpl
|
||||||
const LocalRef<jstring>& error)
|
const LocalRef<jstring>& error)
|
||||||
{
|
{
|
||||||
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
||||||
GlobalRef mid (messageId), e (error);
|
GlobalRef mid (LocalRef<jobject>(messageId.get())), e (LocalRef<jobject>(error.get()));
|
||||||
|
|
||||||
MessageManager::callAsync ([this, mid, e]
|
MessageManager::callAsync ([this, mid, e]
|
||||||
{
|
{
|
||||||
|
|
@ -603,7 +604,7 @@ struct PushNotifications::Pimpl
|
||||||
static LocalRef<jobject> getNotificationManager()
|
static LocalRef<jobject> getNotificationManager()
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
return LocalRef<jobject> (env->CallObjectMethod (context.get(),
|
return LocalRef<jobject> (env->CallObjectMethod (context.get(),
|
||||||
AndroidContext.getSystemService,
|
AndroidContext.getSystemService,
|
||||||
|
|
@ -631,7 +632,7 @@ struct PushNotifications::Pimpl
|
||||||
static LocalRef<jobject> createNotificationBuilder (const PushNotifications::Notification& n)
|
static LocalRef<jobject> createNotificationBuilder (const PushNotifications::Notification& n)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
jclass builderClass = env->FindClass ("android/app/Notification$Builder");
|
jclass builderClass = env->FindClass ("android/app/Notification$Builder");
|
||||||
jassert (builderClass != 0);
|
jassert (builderClass != 0);
|
||||||
|
|
@ -663,7 +664,7 @@ struct PushNotifications::Pimpl
|
||||||
static void setupRequiredFields (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
|
static void setupRequiredFields (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
|
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
|
||||||
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
|
auto notifyIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
|
||||||
|
|
@ -902,7 +903,7 @@ struct PushNotifications::Pimpl
|
||||||
LocalRef<jobject>& notificationBuilder)
|
LocalRef<jobject>& notificationBuilder)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
|
auto activityClass = LocalRef<jobject> (env->CallObjectMethod (context.get(), JavaObject.getClass));
|
||||||
auto deleteIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
|
auto deleteIntent = LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get()));
|
||||||
|
|
@ -930,7 +931,7 @@ struct PushNotifications::Pimpl
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
int actionIndex = 0;
|
int actionIndex = 0;
|
||||||
|
|
||||||
|
|
@ -1025,7 +1026,7 @@ struct PushNotifications::Pimpl
|
||||||
static LocalRef<jobject> juceUrlToAndroidUri (const URL& url)
|
static LocalRef<jobject> juceUrlToAndroidUri (const URL& url)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
|
auto packageNameString = LocalRef<jstring> ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName)));
|
||||||
|
|
||||||
|
|
@ -1351,7 +1352,7 @@ struct PushNotifications::Pimpl
|
||||||
dataDynamicObject->setProperty (juceString (key.get()), juceString (value.get()));
|
dataDynamicObject->setProperty (juceString (key.get()), juceString (value.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataVar (dataDynamicObject);
|
var dataVar (dataDynamicObject.get());
|
||||||
|
|
||||||
DynamicObject::Ptr propertiesDynamicObject = new DynamicObject();
|
DynamicObject::Ptr propertiesDynamicObject = new DynamicObject();
|
||||||
propertiesDynamicObject->setProperty ("collapseKey", juceString (collapseKey.get()));
|
propertiesDynamicObject->setProperty ("collapseKey", juceString (collapseKey.get()));
|
||||||
|
|
@ -1402,7 +1403,7 @@ struct PushNotifications::Pimpl
|
||||||
propertiesDynamicObject->setProperty ("link", link.get() != 0 ? juceString ((jstring) env->CallObjectMethod (link, AndroidUri.toString)) : String());
|
propertiesDynamicObject->setProperty ("link", link.get() != 0 ? juceString ((jstring) env->CallObjectMethod (link, AndroidUri.toString)) : String());
|
||||||
}
|
}
|
||||||
|
|
||||||
n.properties = var (propertiesDynamicObject);
|
n.properties = var (propertiesDynamicObject.get());
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
@ -1492,7 +1493,7 @@ struct PushNotifications::Pimpl
|
||||||
static bool intentActionContainsAnyOf (jobject intent, const StringArray& strings, bool includePackageName)
|
static bool intentActionContainsAnyOf (jobject intent, const StringArray& strings, bool includePackageName)
|
||||||
{
|
{
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
LocalRef<jobject> context (getAppContext());
|
LocalRef<jobject> context (getMainActivity());
|
||||||
|
|
||||||
String packageName = includePackageName ? juceString ((jstring) env->CallObjectMethod (context.get(),
|
String packageName = includePackageName ? juceString ((jstring) env->CallObjectMethod (context.get(),
|
||||||
AndroidContext.getPackageName))
|
AndroidContext.getPackageName))
|
||||||
|
|
@ -1551,6 +1552,70 @@ struct PushNotifications::Pimpl
|
||||||
PushNotifications& owner;
|
PushNotifications& owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME)
|
||||||
|
//==============================================================================
|
||||||
|
struct JuceFirebaseInstanceIdService
|
||||||
|
{
|
||||||
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||||
|
CALLBACK (tokenRefreshed, "firebaseInstanceIdTokenRefreshed", "(Ljava/lang/String;)V")
|
||||||
|
|
||||||
|
DECLARE_JNI_CLASS (InstanceIdService, "com/roli/juce/JuceFirebaseInstanceIdService")
|
||||||
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
|
||||||
|
static void JNICALL tokenRefreshed (void* token)
|
||||||
|
{
|
||||||
|
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
||||||
|
instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JuceFirebaseInstanceIdService::InstanceIdService_Class JuceFirebaseInstanceIdService::InstanceIdService;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
|
||||||
|
//==============================================================================
|
||||||
|
struct JuceFirebaseMessagingService
|
||||||
|
{
|
||||||
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||||
|
CALLBACK (remoteNotificationReceived, "firebaseRemoteMessageReceived", "(Lcom/google/firebase/messaging/RemoteMessage;)V") \
|
||||||
|
CALLBACK (remoteMessagesDeleted, "firebaseRemoteMessagesDeleted", "()V") \
|
||||||
|
CALLBACK (remoteMessageSent, "firebaseRemoteMessageSent", "(Ljava/lang/String;)V") \
|
||||||
|
CALLBACK (remoteMessageSendError, "firebaseRemoteMessageSendError", "(Ljava/lang/String;Ljava/lang/String;)V")
|
||||||
|
|
||||||
|
DECLARE_JNI_CLASS (MessagingService, "com/roli/juce/JuceFirebaseMessagingService")
|
||||||
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
|
||||||
|
static void JNICALL remoteNotificationReceived (JNIEnv*, jobject /*messagingService*/, void* remoteMessage)
|
||||||
|
{
|
||||||
|
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
||||||
|
instance->pimpl->notifyListenersAboutRemoteNotificationFromService (LocalRef<jobject> (static_cast<jobject> (remoteMessage)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void JNICALL remoteMessagesDeleted()
|
||||||
|
{
|
||||||
|
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
||||||
|
instance->pimpl->notifyListenersAboutRemoteNotificationsDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void JNICALL remoteMessageSent (JNIEnv*, jobject /*messagingService*/, void* messageId)
|
||||||
|
{
|
||||||
|
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
||||||
|
instance->pimpl->notifyListenersAboutUpstreamMessageSent (LocalRef<jstring> (static_cast<jstring> (messageId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void JNICALL remoteMessageSendError (JNIEnv*, jobject /*messagingService*/, void* messageId, void* error)
|
||||||
|
{
|
||||||
|
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
||||||
|
instance->pimpl->notifyListenersAboutUpstreamMessageSendingError (LocalRef<jstring> (static_cast<jstring> (messageId)),
|
||||||
|
LocalRef<jstring> (static_cast<jstring> (error)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JuceFirebaseMessagingService::MessagingService_Class JuceFirebaseMessagingService::MessagingService;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
bool juce_handleNotificationIntent (void* intent)
|
bool juce_handleNotificationIntent (void* intent)
|
||||||
{
|
{
|
||||||
auto* instance = PushNotifications::getInstanceWithoutCreating();
|
auto* instance = PushNotifications::getInstanceWithoutCreating();
|
||||||
|
|
@ -1582,35 +1647,25 @@ bool juce_handleNotificationIntent (void* intent)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void juce_firebaseDeviceNotificationsTokenRefreshed (void* token)
|
//==============================================================================
|
||||||
|
struct JuceActivityNewIntentListener
|
||||||
{
|
{
|
||||||
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||||
instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token)));
|
CALLBACK (appNewIntent, "appNewIntent", "(Landroid/content/Intent;)V")
|
||||||
}
|
|
||||||
|
|
||||||
void juce_firebaseRemoteNotificationReceived (void* remoteMessage)
|
DECLARE_JNI_CLASS (JavaActivity, JUCE_PUSH_NOTIFICATIONS_ACTIVITY)
|
||||||
{
|
#undef JNI_CLASS_MEMBERS
|
||||||
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
|
||||||
instance->pimpl->notifyListenersAboutRemoteNotificationFromService (LocalRef<jobject> (static_cast<jobject> (remoteMessage)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void juce_firebaseRemoteMessagesDeleted()
|
static void JNICALL appNewIntent (JNIEnv*, jobject /*activity*/, jobject intentData)
|
||||||
{
|
{
|
||||||
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
#if JUCE_PUSH_NOTIFICATIONS && JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||||
instance->pimpl->notifyListenersAboutRemoteNotificationsDeleted();
|
juce_handleNotificationIntent(static_cast<void*>(intentData));
|
||||||
}
|
#else
|
||||||
|
juce::ignoreUnused(intentData);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void juce_firebaseRemoteMessageSent (void* messageId)
|
JuceActivityNewIntentListener::JavaActivity_Class JuceActivityNewIntentListener::JavaActivity;
|
||||||
{
|
|
||||||
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
|
||||||
instance->pimpl->notifyListenersAboutUpstreamMessageSent (LocalRef<jstring> (static_cast<jstring> (messageId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void juce_firebaseRemoteMessageSendError (void* messageId, void* error)
|
|
||||||
{
|
|
||||||
if (auto* instance = PushNotifications::getInstanceWithoutCreating())
|
|
||||||
instance->pimpl->notifyListenersAboutUpstreamMessageSendingError (LocalRef<jstring> (static_cast<jstring> (messageId)),
|
|
||||||
LocalRef<jstring> (static_cast<jstring> (error)));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue