@@ -0,0 +1,228 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
There's a section below where you can add your own custom code safely, and the | |||||
Projucer will preserve the contents of that block, but the best way to change | |||||
any of these definitions is by using the Projucer's project settings. | |||||
Any commented-out settings will assume their default values. | |||||
*/ | |||||
#pragma once | |||||
//============================================================================== | |||||
// [BEGIN_USER_CODE_SECTION] | |||||
// (You can add your own code in this section, and the Projucer will not overwrite it) | |||||
// [END_USER_CODE_SECTION] | |||||
/* | |||||
============================================================================== | |||||
In accordance with the terms of the JUCE 5 End-Use License Agreement, the | |||||
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered | |||||
ineffective unless you have a JUCE Indie or Pro license, or are using JUCE | |||||
under the GPL v3 license. | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
============================================================================== | |||||
*/ | |||||
// BEGIN SECTION A | |||||
#ifndef JUCE_DISPLAY_SPLASH_SCREEN | |||||
#define JUCE_DISPLAY_SPLASH_SCREEN 0 | |||||
#endif | |||||
#ifndef JUCE_REPORT_APP_USAGE | |||||
#define JUCE_REPORT_APP_USAGE 0 | |||||
#endif | |||||
// END SECTION A | |||||
#define JUCE_USE_DARK_SPLASH_SCREEN 1 | |||||
//============================================================================== | |||||
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_core 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_cryptography 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_data_structures 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_events 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_graphics 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 | |||||
#define JUCE_MODULE_AVAILABLE_juce_opengl 1 | |||||
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1 | |||||
//============================================================================== | |||||
// juce_audio_devices flags: | |||||
#ifndef JUCE_ASIO | |||||
//#define JUCE_ASIO 1 | |||||
#endif | |||||
#ifndef JUCE_WASAPI | |||||
//#define JUCE_WASAPI 1 | |||||
#endif | |||||
#ifndef JUCE_WASAPI_EXCLUSIVE | |||||
//#define JUCE_WASAPI_EXCLUSIVE 1 | |||||
#endif | |||||
#ifndef JUCE_DIRECTSOUND | |||||
//#define JUCE_DIRECTSOUND 1 | |||||
#endif | |||||
#ifndef JUCE_ALSA | |||||
//#define JUCE_ALSA 1 | |||||
#endif | |||||
#ifndef JUCE_JACK | |||||
//#define JUCE_JACK 1 | |||||
#endif | |||||
#ifndef JUCE_USE_ANDROID_OPENSLES | |||||
//#define JUCE_USE_ANDROID_OPENSLES 1 | |||||
#endif | |||||
#ifndef JUCE_USE_WINRT_MIDI | |||||
//#define JUCE_USE_WINRT_MIDI 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_audio_formats flags: | |||||
#ifndef JUCE_USE_FLAC | |||||
//#define JUCE_USE_FLAC 1 | |||||
#endif | |||||
#ifndef JUCE_USE_OGGVORBIS | |||||
//#define JUCE_USE_OGGVORBIS 1 | |||||
#endif | |||||
#ifndef JUCE_USE_MP3AUDIOFORMAT | |||||
//#define JUCE_USE_MP3AUDIOFORMAT 1 | |||||
#endif | |||||
#ifndef JUCE_USE_LAME_AUDIO_FORMAT | |||||
//#define JUCE_USE_LAME_AUDIO_FORMAT 1 | |||||
#endif | |||||
#ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT | |||||
//#define JUCE_USE_WINDOWS_MEDIA_FORMAT 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_audio_processors flags: | |||||
#ifndef JUCE_PLUGINHOST_VST | |||||
//#define JUCE_PLUGINHOST_VST 1 | |||||
#endif | |||||
#ifndef JUCE_PLUGINHOST_VST3 | |||||
//#define JUCE_PLUGINHOST_VST3 1 | |||||
#endif | |||||
#ifndef JUCE_PLUGINHOST_AU | |||||
//#define JUCE_PLUGINHOST_AU 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_core flags: | |||||
#ifndef JUCE_FORCE_DEBUG | |||||
//#define JUCE_FORCE_DEBUG 1 | |||||
#endif | |||||
#ifndef JUCE_LOG_ASSERTIONS | |||||
//#define JUCE_LOG_ASSERTIONS 1 | |||||
#endif | |||||
#ifndef JUCE_CHECK_MEMORY_LEAKS | |||||
//#define JUCE_CHECK_MEMORY_LEAKS 1 | |||||
#endif | |||||
#ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||||
//#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 1 | |||||
#endif | |||||
#ifndef JUCE_INCLUDE_ZLIB_CODE | |||||
//#define JUCE_INCLUDE_ZLIB_CODE 1 | |||||
#endif | |||||
#ifndef JUCE_USE_CURL | |||||
//#define JUCE_USE_CURL 1 | |||||
#endif | |||||
#ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||||
//#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 | |||||
#endif | |||||
#ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES | |||||
//#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_events flags: | |||||
#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK | |||||
//#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_graphics flags: | |||||
#ifndef JUCE_USE_COREIMAGE_LOADER | |||||
//#define JUCE_USE_COREIMAGE_LOADER 1 | |||||
#endif | |||||
#ifndef JUCE_USE_DIRECTWRITE | |||||
//#define JUCE_USE_DIRECTWRITE 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_gui_basics flags: | |||||
#ifndef JUCE_ENABLE_REPAINT_DEBUGGING | |||||
//#define JUCE_ENABLE_REPAINT_DEBUGGING 1 | |||||
#endif | |||||
#ifndef JUCE_USE_XSHM | |||||
//#define JUCE_USE_XSHM 1 | |||||
#endif | |||||
#ifndef JUCE_USE_XRENDER | |||||
//#define JUCE_USE_XRENDER 1 | |||||
#endif | |||||
#ifndef JUCE_USE_XCURSOR | |||||
//#define JUCE_USE_XCURSOR 1 | |||||
#endif | |||||
//============================================================================== | |||||
// juce_gui_extra flags: | |||||
#ifndef JUCE_WEB_BROWSER | |||||
//#define JUCE_WEB_BROWSER 1 | |||||
#endif | |||||
#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR | |||||
//#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1 | |||||
#endif | |||||
//============================================================================== | |||||
#ifndef JUCE_STANDALONE_APPLICATION | |||||
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone) | |||||
#define JUCE_STANDALONE_APPLICATION JucePlugin_Build_Standalone | |||||
#else | |||||
#define JUCE_STANDALONE_APPLICATION 1 | |||||
#endif | |||||
#endif |
@@ -0,0 +1,80 @@ | |||||
/* ========================================================================================= | |||||
This is an auto-generated file: Any edits you make may be overwritten! | |||||
*/ | |||||
#pragma once | |||||
namespace BinaryData | |||||
{ | |||||
extern const char* ic_stat_name_png; | |||||
const int ic_stat_name_pngSize = 351; | |||||
extern const char* ic_stat_name2_png; | |||||
const int ic_stat_name2_pngSize = 204; | |||||
extern const char* ic_stat_name3_png; | |||||
const int ic_stat_name3_pngSize = 292; | |||||
extern const char* ic_stat_name4_png; | |||||
const int ic_stat_name4_pngSize = 341; | |||||
extern const char* ic_stat_name5_png; | |||||
const int ic_stat_name5_pngSize = 337; | |||||
extern const char* ic_stat_name6_png; | |||||
const int ic_stat_name6_pngSize = 1059; | |||||
extern const char* ic_stat_name7_png; | |||||
const int ic_stat_name7_pngSize = 543; | |||||
extern const char* ic_stat_name8_png; | |||||
const int ic_stat_name8_pngSize = 928; | |||||
extern const char* ic_stat_name9_png; | |||||
const int ic_stat_name9_pngSize = 915; | |||||
extern const char* ic_stat_name10_png; | |||||
const int ic_stat_name10_pngSize = 1156; | |||||
extern const char* demonstrative_caf; | |||||
const int demonstrative_cafSize = 190178; | |||||
extern const char* demonstrative_mp3; | |||||
const int demonstrative_mp3Size = 127059; | |||||
extern const char* isntit_caf; | |||||
const int isntit_cafSize = 20654; | |||||
extern const char* isntit_mp3; | |||||
const int isntit_mp3Size = 11284; | |||||
extern const char* jinglebellssms_caf; | |||||
const int jinglebellssms_cafSize = 116568; | |||||
extern const char* jinglebellssms_mp3; | |||||
const int jinglebellssms_mp3Size = 24116; | |||||
extern const char* served_caf; | |||||
const int served_cafSize = 13922; | |||||
extern const char* served_mp3; | |||||
const int served_mp3Size = 6687; | |||||
extern const char* solemn_caf; | |||||
const int solemn_cafSize = 114902; | |||||
extern const char* solemn_mp3; | |||||
const int solemn_mp3Size = 75650; | |||||
// Points to the start of a list of resource names. | |||||
extern const char* namedResourceList[]; | |||||
// Number of elements in the namedResourceList array. | |||||
const int namedResourceListSize = 20; | |||||
// If you provide the name of one of the binary resource variables above, this function will | |||||
// return the corresponding data and its size (or a null pointer if the name isn't found). | |||||
const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes) throw(); | |||||
} |
@@ -0,0 +1,45 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
This is the header file that your files should include in order to get all the | |||||
JUCE library headers. You should avoid including the JUCE headers directly in | |||||
your own source files, because that wouldn't pick up the correct configuration | |||||
options for your app. | |||||
*/ | |||||
#pragma once | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_basics/juce_audio_basics.h> | |||||
#include <juce_audio_devices/juce_audio_devices.h> | |||||
#include <juce_audio_formats/juce_audio_formats.h> | |||||
#include <juce_audio_processors/juce_audio_processors.h> | |||||
#include <juce_core/juce_core.h> | |||||
#include <juce_cryptography/juce_cryptography.h> | |||||
#include <juce_data_structures/juce_data_structures.h> | |||||
#include <juce_events/juce_events.h> | |||||
#include <juce_graphics/juce_graphics.h> | |||||
#include <juce_gui_basics/juce_gui_basics.h> | |||||
#include <juce_gui_extra/juce_gui_extra.h> | |||||
#include <juce_opengl/juce_opengl.h> | |||||
#include "BinaryData.h" | |||||
#if ! DONT_SET_USING_JUCE_NAMESPACE | |||||
// If your code uses a lot of JUCE classes, then this will obviously save you | |||||
// a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. | |||||
using namespace juce; | |||||
#endif | |||||
#if ! JUCE_DONT_DECLARE_PROJECTINFO | |||||
namespace ProjectInfo | |||||
{ | |||||
const char* const projectName = "PushNotificationsDemo"; | |||||
const char* const versionString = "1.0.0"; | |||||
const int versionNumber = 0x10000; | |||||
} | |||||
#endif |
@@ -0,0 +1,12 @@ | |||||
Important Note!! | |||||
================ | |||||
The purpose of this folder is to contain files that are auto-generated by the Projucer, | |||||
and ALL files in this folder will be mercilessly DELETED and completely re-written whenever | |||||
the Projucer saves your project. | |||||
Therefore, it's a bad idea to make any manual changes to the files in here, or to | |||||
put any of your own files in here if you don't want to lose them. (Of course you may choose | |||||
to add the folder's contents to your version-control system so that you can re-merge your own | |||||
modifications after the Projucer has saved its changes). |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_basics/juce_audio_basics.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_basics/juce_audio_basics.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_devices/juce_audio_devices.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_devices/juce_audio_devices.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_formats/juce_audio_formats.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_formats/juce_audio_formats.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_processors/juce_audio_processors.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_audio_processors/juce_audio_processors.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_core/juce_core.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_core/juce_core.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_cryptography/juce_cryptography.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_cryptography/juce_cryptography.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_data_structures/juce_data_structures.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_data_structures/juce_data_structures.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_events/juce_events.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_events/juce_events.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_graphics/juce_graphics.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_graphics/juce_graphics.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_gui_basics/juce_gui_basics.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_gui_basics/juce_gui_basics.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_gui_extra/juce_gui_extra.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_gui_extra/juce_gui_extra.mm> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_opengl/juce_opengl.cpp> |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
IMPORTANT! This file is auto-generated each time you save your | |||||
project - if you alter its contents, your changes may be overwritten! | |||||
*/ | |||||
#include "AppConfig.h" | |||||
#include <juce_opengl/juce_opengl.mm> |
@@ -0,0 +1,161 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<JUCERPROJECT id="SS6MsF" name="PushNotificationsDemo" displaySplashScreen="0" | |||||
reportAppUsage="0" splashScreenColour="Dark" projectType="guiapp" | |||||
version="1.0.0" bundleIdentifier="com.juce.pushnotificationsdemo" | |||||
includeBinaryInAppConfig="1" cppLanguageStandard="11" jucerVersion="5.1.2"> | |||||
<MAINGROUP id="VaJRhp" name="PushNotificationsDemo"> | |||||
<GROUP id="{712B2C90-FAC6-CCF3-F8F9-ED82411AF5ED}" name="BinaryResources"> | |||||
<GROUP id="{DBF05B65-21C4-E232-D005-149CC3FD4CD6}" name="images"> | |||||
<FILE id="vPFlgX" name="ic_stat_name.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name.png"/> | |||||
<FILE id="cFrr9F" name="ic_stat_name2.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name2.png"/> | |||||
<FILE id="d6aZX5" name="ic_stat_name3.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name3.png"/> | |||||
<FILE id="nkb99Z" name="ic_stat_name4.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name4.png"/> | |||||
<FILE id="y7x7U0" name="ic_stat_name5.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name5.png"/> | |||||
<FILE id="IVufcJ" name="ic_stat_name6.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name6.png"/> | |||||
<FILE id="yYDjas" name="ic_stat_name7.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name7.png"/> | |||||
<FILE id="uB2QTS" name="ic_stat_name8.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name8.png"/> | |||||
<FILE id="Jgbx8i" name="ic_stat_name9.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name9.png"/> | |||||
<FILE id="Q2zkvf" name="ic_stat_name10.png" compile="0" resource="1" | |||||
file="BinaryResources/images/ic_stat_name10.png"/> | |||||
</GROUP> | |||||
<GROUP id="{D7B3F6B4-2E9A-E281-831A-2B67D6D4C4A0}" name="sounds"> | |||||
<FILE id="il8LpK" name="demonstrative.caf" compile="0" resource="1" | |||||
file="BinaryResources/sounds/demonstrative.caf"/> | |||||
<FILE id="aUWCPt" name="demonstrative.mp3" compile="0" resource="1" | |||||
file="BinaryResources/sounds/demonstrative.mp3"/> | |||||
<FILE id="PWlDpi" name="isntit.caf" compile="0" resource="1" file="BinaryResources/sounds/isntit.caf"/> | |||||
<FILE id="nbNoVQ" name="isntit.mp3" compile="0" resource="1" file="BinaryResources/sounds/isntit.mp3"/> | |||||
<FILE id="AheEYO" name="jinglebellssms.caf" compile="0" resource="1" | |||||
file="BinaryResources/sounds/jinglebellssms.caf"/> | |||||
<FILE id="WeQemY" name="jinglebellssms.mp3" compile="0" resource="1" | |||||
file="BinaryResources/sounds/jinglebellssms.mp3"/> | |||||
<FILE id="TmLrYM" name="served.caf" compile="0" resource="1" file="BinaryResources/sounds/served.caf"/> | |||||
<FILE id="Ad45fH" name="served.mp3" compile="0" resource="1" file="BinaryResources/sounds/served.mp3"/> | |||||
<FILE id="qH4XUR" name="solemn.caf" compile="0" resource="1" file="BinaryResources/sounds/solemn.caf"/> | |||||
<FILE id="cwrEXS" name="solemn.mp3" compile="0" resource="1" file="BinaryResources/sounds/solemn.mp3"/> | |||||
</GROUP> | |||||
</GROUP> | |||||
<GROUP id="{1AF8C966-743E-3F06-189C-AADD83767CB4}" name="Source"> | |||||
<FILE id="f0GZqm" name="MainComponent.cpp" compile="1" resource="0" | |||||
file="Source/MainComponent.cpp"/> | |||||
<FILE id="p0ifBx" name="MainComponent.h" compile="0" resource="0" file="Source/MainComponent.h"/> | |||||
<FILE id="nD1eGt" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/> | |||||
</GROUP> | |||||
</MAINGROUP> | |||||
<EXPORTFORMATS> | |||||
<XCODE_IPHONE targetFolder="Builds/iOS" iosScreenOrientation="portraitlandscape" | |||||
iosPushNotifications="1" extraCompilerFlags="-pedantic -Werror -Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -F../../../../3rd_party/FacebookSDKs-iOS-4 -F../../../../3rd_party/Firebase_ios_sdk/Analytics -F../../../../3rd_party/Firebase_ios_sdk/Messaging" | |||||
customXcodeResourceFolders="./BinaryResources/sounds"> | |||||
<CONFIGURATIONS> | |||||
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="PushNotificationsDemo"/> | |||||
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="PushNotificationsDemo"/> | |||||
</CONFIGURATIONS> | |||||
<MODULEPATHS> | |||||
<MODULEPATH id="juce_core" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_events" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_graphics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_data_structures" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_extra" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_cryptography" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_opengl" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_devices" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_formats" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_processors" path="../Repos/roli-software/JUCE/modules"/> | |||||
</MODULEPATHS> | |||||
</XCODE_IPHONE> | |||||
<ANDROIDSTUDIO targetFolder="Builds/Android" androidSDKPath="" androidNDKPath="" | |||||
androidEnableRemoteNotifications="1" androidRemoteNotificationsConfigFile="../google-services.json" | |||||
extraCompilerFlags="-pedantic -Werror -Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -F../../../../3rd_party/FacebookSDKs-iOS-4 -F../../../../3rd_party/Firebase_ios_sdk/Analytics -F../../../../3rd_party/Firebase_ios_sdk/Messaging" | |||||
androidVibratePermissionNeeded="1" androidMinimumSDK="26"> | |||||
<CONFIGURATIONS> | |||||
<CONFIGURATION name="Debug" androidArchitectures="armeabi x86" isDebug="1" optimisation="1" | |||||
targetName="PushNotificationsDemo" androidAdditionalRawValueResources="../BinaryResources/sounds/demonstrative.mp3 ../BinaryResources/sounds/isntit.mp3 ../BinaryResources/sounds/jinglebellssms.mp3 ../BinaryResources/sounds/served.mp3 ../BinaryResources/sounds/solemn.mp3 ../BinaryResources/images/ic_stat_name.png ../BinaryResources/images/ic_stat_name2.png ../BinaryResources/images/ic_stat_name3.png ../BinaryResources/images/ic_stat_name4.png ../BinaryResources/images/ic_stat_name5.png ../BinaryResources/images/ic_stat_name6.png ../BinaryResources/images/ic_stat_name7.png ../BinaryResources/images/ic_stat_name8.png ../BinaryResources/images/ic_stat_name9.png ../BinaryResources/images/ic_stat_name10.png"/> | |||||
<CONFIGURATION name="Release" androidArchitectures="" isDebug="0" optimisation="3" | |||||
targetName="PushNotificationsDemo" androidAdditionalRawValueResources="../BinaryResources/sounds/demonstrative.mp3 ../BinaryResources/sounds/isntit.mp3 ../BinaryResources/sounds/jinglebellssms.mp3 ../BinaryResources/sounds/served.mp3 ../BinaryResources/sounds/solemn.mp3 ../BinaryResources/images/ic_stat_name.png ../BinaryResources/images/ic_stat_name2.png ../BinaryResources/images/ic_stat_name3.png ../BinaryResources/images/ic_stat_name4.png ../BinaryResources/images/ic_stat_name5.png ../BinaryResources/images/ic_stat_name6.png ../BinaryResources/images/ic_stat_name7.png ../BinaryResources/images/ic_stat_name8.png ../BinaryResources/images/ic_stat_name9.png ../BinaryResources/images/ic_stat_name10.png"/> | |||||
</CONFIGURATIONS> | |||||
<MODULEPATHS> | |||||
<MODULEPATH id="juce_core" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_events" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_graphics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_data_structures" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_extra" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_cryptography" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_opengl" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_devices" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_formats" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_processors" path="../Repos/roli-software/JUCE/modules"/> | |||||
</MODULEPATHS> | |||||
</ANDROIDSTUDIO> | |||||
<XCODE_MAC targetFolder="Builds/MacOSX"> | |||||
<CONFIGURATIONS> | |||||
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="PushNotificationsDemo"/> | |||||
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="PushNotificationsDemo"/> | |||||
</CONFIGURATIONS> | |||||
<MODULEPATHS> | |||||
<MODULEPATH id="juce_opengl" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_extra" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_graphics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_events" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_data_structures" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_cryptography" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_core" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_processors" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_formats" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_devices" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
</MODULEPATHS> | |||||
</XCODE_MAC> | |||||
<VS2015 targetFolder="Builds/VisualStudio2015"> | |||||
<CONFIGURATIONS> | |||||
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||||
isDebug="1" optimisation="1" targetName="PushNotificationsDemo"/> | |||||
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||||
isDebug="0" optimisation="3" targetName="PushNotificationsDemo"/> | |||||
</CONFIGURATIONS> | |||||
<MODULEPATHS> | |||||
<MODULEPATH id="juce_opengl" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_extra" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_gui_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_graphics" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_events" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_data_structures" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_cryptography" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_core" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_processors" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_formats" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_devices" path="../Repos/roli-software/JUCE/modules"/> | |||||
<MODULEPATH id="juce_audio_basics" path="../Repos/roli-software/JUCE/modules"/> | |||||
</MODULEPATHS> | |||||
</VS2015> | |||||
</EXPORTFORMATS> | |||||
<MODULES> | |||||
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/> | |||||
</MODULES> | |||||
<JUCEOPTIONS/> | |||||
</JUCERPROJECT> |
@@ -0,0 +1,96 @@ | |||||
/* | |||||
============================================================================== | |||||
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. | |||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
27th April 2017). | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
Or: You may also use this code under the terms of the GPL v3 (see | |||||
www.gnu.org/licenses). | |||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||||
DISCLAIMED. | |||||
============================================================================== | |||||
*/ | |||||
#include "../JuceLibraryCode/JuceHeader.h" | |||||
#include "MainComponent.h" | |||||
//============================================================================== | |||||
class PushNotificationsDemoApplication : public JUCEApplication | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
PushNotificationsDemoApplication() {} | |||||
const String getApplicationName() override { return ProjectInfo::projectName; } | |||||
const String getApplicationVersion() override { return ProjectInfo::versionString; } | |||||
bool moreThanOneInstanceAllowed() override { return true; } | |||||
//============================================================================== | |||||
void initialise (const String& commandLine) override | |||||
{ | |||||
ignoreUnused (commandLine); | |||||
mainWindow = new MainWindow (getApplicationName()); | |||||
} | |||||
void shutdown() override | |||||
{ | |||||
mainWindow = nullptr; | |||||
} | |||||
//============================================================================== | |||||
void systemRequestedQuit() override | |||||
{ | |||||
quit(); | |||||
} | |||||
void anotherInstanceStarted (const String& commandLine) override | |||||
{ | |||||
ignoreUnused (commandLine); | |||||
} | |||||
//============================================================================== | |||||
class MainWindow : public DocumentWindow | |||||
{ | |||||
public: | |||||
MainWindow (String name) : DocumentWindow (name, | |||||
Desktop::getInstance().getDefaultLookAndFeel() | |||||
.findColour (ResizableWindow::backgroundColourId), | |||||
DocumentWindow::allButtons) | |||||
{ | |||||
setUsingNativeTitleBar (true); | |||||
setContentOwned (new MainContentComponent(), true); | |||||
centreWithSize (getWidth(), getHeight()); | |||||
setVisible (true); | |||||
} | |||||
void closeButtonPressed() override | |||||
{ | |||||
JUCEApplication::getInstance()->systemRequestedQuit(); | |||||
} | |||||
private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | |||||
}; | |||||
private: | |||||
ScopedPointer<MainWindow> mainWindow; | |||||
}; | |||||
//============================================================================== | |||||
START_JUCE_APPLICATION (PushNotificationsDemoApplication) |
@@ -0,0 +1,628 @@ | |||||
/* | |||||
============================================================================== | |||||
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. | |||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
27th April 2017). | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
Or: You may also use this code under the terms of the GPL v3 (see | |||||
www.gnu.org/licenses). | |||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||||
DISCLAIMED. | |||||
============================================================================== | |||||
*/ | |||||
#include "MainComponent.h" | |||||
//============================================================================== | |||||
MainContentComponent::MainContentComponent() | |||||
{ | |||||
#if JUCE_ANDROID || JUCE_IOS | |||||
addAndMakeVisible (headerLabel); | |||||
addAndMakeVisible (mainTabs); | |||||
addAndMakeVisible (sendButton); | |||||
#else | |||||
addAndMakeVisible (notAvailableYetLabel); | |||||
#endif | |||||
headerLabel.setJustificationType (Justification::centred); | |||||
notAvailableYetLabel.setJustificationType (Justification::centred); | |||||
const auto colour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId); | |||||
localNotificationsTabs.addTab ("Req. params", colour, &requiredParamsView, false); | |||||
localNotificationsTabs.addTab ("Opt. params1", colour, &optionalParamsOneView, false); | |||||
#if JUCE_ANDROID | |||||
localNotificationsTabs.addTab ("Opt. params2", colour, &optionalParamsTwoView, false); | |||||
localNotificationsTabs.addTab ("Opt. params3", colour, &optionalParamsThreeView, false); | |||||
#endif | |||||
localNotificationsTabs.addTab ("Aux. actions", colour, &auxActionsView, false); | |||||
mainTabs.addTab ("Local", colour, &localNotificationsTabs, false); | |||||
mainTabs.addTab ("Remote", colour, &remoteView, false); | |||||
const auto userArea = Desktop::getInstance().getDisplays().getMainDisplay().userArea; | |||||
#if JUCE_ANDROID || JUCE_IOS | |||||
setSize (userArea.getWidth(), userArea.getHeight()); | |||||
#else | |||||
setSize (userArea.getWidth() / 2, userArea.getHeight() / 2); | |||||
#endif | |||||
sendButton.addListener (this); | |||||
auxActionsView.getDeliveredNotificationsButton .addListener (this); | |||||
auxActionsView.removeDeliveredNotifWithIdButton.addListener (this); | |||||
auxActionsView.removeAllDeliveredNotifsButton .addListener (this); | |||||
#if JUCE_IOS | |||||
auxActionsView.getPendingNotificationsButton .addListener (this); | |||||
auxActionsView.removePendingNotifWithIdButton.addListener (this); | |||||
auxActionsView.removeAllPendingNotifsButton .addListener (this); | |||||
#endif | |||||
remoteView.getDeviceTokenButton .addListener (this); | |||||
remoteView.sendRemoteMessageButton .addListener (this); | |||||
remoteView.subscribeToSportsButton .addListener (this); | |||||
remoteView.unsubscribeFromSportsButton.addListener (this); | |||||
optionalParamsThreeView.accentColourButton.addListener (this); | |||||
optionalParamsThreeView.ledColourButton .addListener (this); | |||||
jassert (PushNotifications::getInstance()->areNotificationsEnabled()); | |||||
PushNotifications::getInstance()->addListener (this); | |||||
#if JUCE_IOS | |||||
optionalParamsOneView.fireInComboBox.addListener (this); | |||||
PushNotifications::getInstance()->requestPermissionsWithSettings (getIosSettings()); | |||||
#elif JUCE_ANDROID | |||||
PushNotifications::ChannelGroup cg { "demoGroup", "demo group" }; | |||||
PushNotifications::getInstance()->setupChannels ({{ cg }}, getAndroidChannels()); | |||||
#endif | |||||
} | |||||
MainContentComponent::~MainContentComponent() | |||||
{ | |||||
PushNotifications::getInstance()->removeListener (this); | |||||
} | |||||
void MainContentComponent::paint (Graphics& g) | |||||
{ | |||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); | |||||
} | |||||
void MainContentComponent::resized() | |||||
{ | |||||
auto bounds = getLocalBounds().reduced (getWidth() / 20, getHeight() / 40); | |||||
headerLabel.setBounds (bounds.removeFromTop (bounds.proportionOfHeight (0.1f))); | |||||
mainTabs.setBounds (bounds.removeFromTop (bounds.proportionOfHeight (0.8f))); | |||||
sendButton.setBounds (bounds); | |||||
notAvailableYetLabel.setBounds (getLocalBounds()); | |||||
} | |||||
void MainContentComponent::buttonClicked (Button* b) | |||||
{ | |||||
if (b == &sendButton) | |||||
sendLocalNotification(); | |||||
else if (b == &optionalParamsThreeView.accentColourButton) | |||||
setupAccentColour(); | |||||
else if (b == &optionalParamsThreeView.ledColourButton) | |||||
setupLedColour(); | |||||
else if (b == &auxActionsView.getDeliveredNotificationsButton) | |||||
getDeliveredNotifications(); | |||||
else if (b == &auxActionsView.removeDeliveredNotifWithIdButton) | |||||
PushNotifications::getInstance()->removeDeliveredNotification (auxActionsView.deliveredNotifIdentifier.getText()); | |||||
else if (b == &auxActionsView.removeAllDeliveredNotifsButton) | |||||
PushNotifications::getInstance()->removeAllDeliveredNotifications(); | |||||
#if JUCE_IOS | |||||
else if (b == &auxActionsView.getPendingNotificationsButton) | |||||
PushNotifications::getInstance()->getPendingLocalNotifications(); | |||||
else if (b == &auxActionsView.removePendingNotifWithIdButton) | |||||
PushNotifications::getInstance()->removePendingLocalNotification (auxActionsView.pendingNotifIdentifier.getText()); | |||||
else if (b == &auxActionsView.removeAllPendingNotifsButton) | |||||
PushNotifications::getInstance()->removeAllPendingLocalNotifications(); | |||||
#endif | |||||
else if (b == &remoteView.getDeviceTokenButton) | |||||
{ | |||||
String token = PushNotifications::getInstance()->getDeviceToken(); | |||||
DBG ("token = " + token); | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Device token", token); | |||||
} | |||||
#if JUCE_ANDROID | |||||
else if (b == &remoteView.sendRemoteMessageButton) | |||||
{ | |||||
StringPairArray data; | |||||
data.set ("key1", "value1"); | |||||
data.set ("key2", "value2"); | |||||
static int id = 100; | |||||
PushNotifications::getInstance()->sendUpstreamMessage ("872047750958", | |||||
"com.juce.pushnotificationsdemo", | |||||
String (id++), | |||||
"standardType", | |||||
3600, | |||||
data); | |||||
} | |||||
else if (b == &remoteView.subscribeToSportsButton) | |||||
{ | |||||
PushNotifications::getInstance()->subscribeToTopic ("sports"); | |||||
} | |||||
else if (b == &remoteView.unsubscribeFromSportsButton) | |||||
{ | |||||
PushNotifications::getInstance()->unsubscribeFromTopic ("sports"); | |||||
} | |||||
#endif | |||||
} | |||||
void MainContentComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) | |||||
{ | |||||
#if JUCE_IOS | |||||
if (comboBoxThatHasChanged == &optionalParamsOneView.fireInComboBox) | |||||
{ | |||||
const bool repeatsAllowed = optionalParamsOneView.fireInComboBox.getSelectedItemIndex() >= 6; | |||||
optionalParamsOneView.repeatButton.setEnabled (repeatsAllowed); | |||||
if (! repeatsAllowed) | |||||
optionalParamsOneView.repeatButton.setToggleState (false, NotificationType::sendNotification); | |||||
} | |||||
#else | |||||
ignoreUnused (comboBoxThatHasChanged); | |||||
#endif | |||||
} | |||||
void MainContentComponent::sendLocalNotification() | |||||
{ | |||||
PushNotifications::Notification n; | |||||
fillRequiredParams (n); | |||||
fillOptionalParamsOne (n); | |||||
#if JUCE_ANDROID | |||||
fillOptionalParamsTwo (n); | |||||
fillOptionalParamsThree (n); | |||||
#endif | |||||
if (! n.isValid()) | |||||
{ | |||||
#if JUCE_IOS | |||||
String requiredFields = "identifier (from iOS 10), title, body and category"; | |||||
#elif JUCE_ANDROID | |||||
String requiredFields = "channel ID (from Android O), title, body and icon"; | |||||
#else | |||||
String requiredFields = "all required fields"; | |||||
#endif | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Incorrect notifications setup", | |||||
"Please make sure that " | |||||
+ requiredFields + " are set."); | |||||
return; | |||||
} | |||||
PushNotifications::getInstance()->sendLocalNotification (n); | |||||
} | |||||
void MainContentComponent::fillRequiredParams (PushNotifications::Notification& n) | |||||
{ | |||||
n.identifier = requiredParamsView.identifierEditor.getText(); | |||||
n.title = requiredParamsView.titleEditor.getText(); | |||||
n.body = requiredParamsView.bodyEditor.getText(); | |||||
#if JUCE_IOS | |||||
n.category = requiredParamsView.categories[requiredParamsView.categoryComboBox.getSelectedItemIndex()]; | |||||
#elif JUCE_ANDROID | |||||
if (requiredParamsView.iconComboBox.getSelectedItemIndex() == 0) | |||||
n.icon = "ic_stat_name"; | |||||
else if (requiredParamsView.iconComboBox.getSelectedItemIndex() == 1) | |||||
n.icon = "ic_stat_name2"; | |||||
else if (requiredParamsView.iconComboBox.getSelectedItemIndex() == 2) | |||||
n.icon = "ic_stat_name3"; | |||||
else if (requiredParamsView.iconComboBox.getSelectedItemIndex() == 3) | |||||
n.icon = "ic_stat_name4"; | |||||
else | |||||
n.icon = "ic_stat_name5"; | |||||
// Note: this is not strictly speaking required param, just doing it here because it is the fastest way! | |||||
n.publicVersion = new PushNotifications::Notification(); | |||||
n.publicVersion->identifier = "blahblahblah"; | |||||
n.publicVersion->title = "Public title!"; | |||||
n.publicVersion->body = "Public body!"; | |||||
n.publicVersion->icon = n.icon; | |||||
#if __ANDROID_API__ >= 26 | |||||
n.channelId = String (requiredParamsView.channelIdComboBox.getSelectedItemIndex() + 1); | |||||
#endif | |||||
#endif | |||||
} | |||||
void MainContentComponent::fillOptionalParamsOne (PushNotifications::Notification& n) | |||||
{ | |||||
n.subtitle = optionalParamsOneView.subtitleEditor.getText(); | |||||
n.badgeNumber = optionalParamsOneView.badgeNumberComboBox.getSelectedItemIndex(); | |||||
if (optionalParamsOneView.soundToPlayComboBox.getSelectedItemIndex() > 0) | |||||
n.soundToPlay = URL (optionalParamsOneView.soundToPlayComboBox.getItemText (optionalParamsOneView.soundToPlayComboBox.getSelectedItemIndex())); | |||||
n.properties = JSON::parse (optionalParamsOneView.propertiesEditor.getText()); | |||||
#if JUCE_IOS | |||||
n.triggerIntervalSec = double (optionalParamsOneView.fireInComboBox.getSelectedItemIndex() * 10); | |||||
n.repeat = optionalParamsOneView.repeatButton.getToggleState(); | |||||
#elif JUCE_ANDROID | |||||
if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 1) | |||||
n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name6_png, BinaryData::ic_stat_name6_pngSize); | |||||
else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 2) | |||||
n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name7_png, BinaryData::ic_stat_name7_pngSize); | |||||
else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 3) | |||||
n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name8_png, BinaryData::ic_stat_name8_pngSize); | |||||
else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 4) | |||||
n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name9_png, BinaryData::ic_stat_name9_pngSize); | |||||
else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 5) | |||||
n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name10_png, BinaryData::ic_stat_name10_pngSize); | |||||
n.badgeIconType = (PushNotifications::Notification::BadgeIconType) optionalParamsOneView.badgeIconComboBox.getSelectedItemIndex(); | |||||
n.tickerText = optionalParamsOneView.tickerTextEditor.getText(); | |||||
n.shouldAutoCancel = optionalParamsOneView.autoCancelButton.getToggleState(); | |||||
n.alertOnlyOnce = optionalParamsOneView.alertOnlyOnceButton.getToggleState(); | |||||
if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 1) | |||||
{ | |||||
PushNotifications::Notification::Action a, a2; | |||||
a .style = PushNotifications::Notification::Action::button; | |||||
a2.style = PushNotifications::Notification::Action::button; | |||||
a .title = "Ok"; | |||||
a2.title = "Cancel"; | |||||
n.actions.add (a); | |||||
n.actions.add (a2); | |||||
} | |||||
else if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 2) | |||||
{ | |||||
PushNotifications::Notification::Action a, a2; | |||||
a .title = "Ok"; | |||||
a2.title = "Cancel"; | |||||
a .style = PushNotifications::Notification::Action::button; | |||||
a2.style = PushNotifications::Notification::Action::button; | |||||
a .icon = "ic_stat_name4"; | |||||
a2.icon = "ic_stat_name5"; | |||||
n.actions.add (a); | |||||
n.actions.add (a2); | |||||
} | |||||
else if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 3) | |||||
{ | |||||
PushNotifications::Notification::Action a, a2; | |||||
a .title = "Input Text Here"; | |||||
a2.title = "No"; | |||||
a .style = PushNotifications::Notification::Action::text; | |||||
a2.style = PushNotifications::Notification::Action::button; | |||||
a .icon = "ic_stat_name4"; | |||||
a2.icon = "ic_stat_name5"; | |||||
a.textInputPlaceholder = "placeholder text ..."; | |||||
n.actions.add (a); | |||||
n.actions.add (a2); | |||||
} | |||||
else if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 4) | |||||
{ | |||||
PushNotifications::Notification::Action a, a2; | |||||
a .title = "Input Text Here"; | |||||
a2.title = "No"; | |||||
a .style = PushNotifications::Notification::Action::text; | |||||
a2.style = PushNotifications::Notification::Action::button; | |||||
a .icon = "ic_stat_name4"; | |||||
a2.icon = "ic_stat_name5"; | |||||
a.textInputPlaceholder = "placeholder text ..."; | |||||
a.allowedResponses.add ("Response 1"); | |||||
a.allowedResponses.add ("Response 2"); | |||||
a.allowedResponses.add ("Response 3"); | |||||
n.actions.add (a); | |||||
n.actions.add (a2); | |||||
} | |||||
#endif | |||||
} | |||||
void MainContentComponent::fillOptionalParamsTwo (PushNotifications::Notification& n) | |||||
{ | |||||
using Notification = PushNotifications::Notification; | |||||
Notification::Progress progress; | |||||
progress.max = optionalParamsTwoView.progressMaxComboBox.getSelectedItemIndex() * 10; | |||||
progress.current = optionalParamsTwoView.progressCurrentComboBox.getSelectedItemIndex() * 10; | |||||
progress.indeterminate = optionalParamsTwoView.progressIndeterminateButton.getToggleState(); | |||||
n.progress = progress; | |||||
n.person = optionalParamsTwoView.personEditor.getText(); | |||||
n.type = Notification::Type (optionalParamsTwoView.categoryComboBox.getSelectedItemIndex()); | |||||
n.priority = Notification::Priority (optionalParamsTwoView.priorityComboBox.getSelectedItemIndex() - 2); | |||||
n.lockScreenAppearance = Notification::LockScreenAppearance (optionalParamsTwoView.lockScreenVisibilityComboBox.getSelectedItemIndex() - 1); | |||||
n.groupId = optionalParamsTwoView.groupIdEditor.getText(); | |||||
n.groupSortKey = optionalParamsTwoView.sortKeyEditor.getText(); | |||||
n.groupSummary = optionalParamsTwoView.groupSummaryButton.getToggleState(); | |||||
n.groupAlertBehaviour = Notification::GroupAlertBehaviour (optionalParamsTwoView.groupAlertBehaviourComboBox.getSelectedItemIndex()); | |||||
} | |||||
void MainContentComponent::fillOptionalParamsThree (PushNotifications::Notification& n) | |||||
{ | |||||
n.accentColour = optionalParamsThreeView.accentColourButton.findColour (TextButton::buttonColourId, false); | |||||
n.ledColour = optionalParamsThreeView.ledColourButton .findColour (TextButton::buttonColourId, false); | |||||
using Notification = PushNotifications::Notification; | |||||
Notification::LedBlinkPattern ledBlinkPattern; | |||||
ledBlinkPattern.msToBeOn = optionalParamsThreeView.ledMsToBeOnComboBox .getSelectedItemIndex() * 200; | |||||
ledBlinkPattern.msToBeOff = optionalParamsThreeView.ledMsToBeOffComboBox.getSelectedItemIndex() * 200; | |||||
n.ledBlinkPattern = ledBlinkPattern; | |||||
Array<int> vibrationPattern; | |||||
if (optionalParamsThreeView.vibratorMsToBeOnComboBox .getSelectedItemIndex() > 0 && | |||||
optionalParamsThreeView.vibratorMsToBeOffComboBox.getSelectedItemIndex() > 0) | |||||
{ | |||||
vibrationPattern.add (optionalParamsThreeView.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500); | |||||
vibrationPattern.add (optionalParamsThreeView.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500); | |||||
vibrationPattern.add (2 * optionalParamsThreeView.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500); | |||||
vibrationPattern.add (2 * optionalParamsThreeView.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500); | |||||
} | |||||
n.vibrationPattern = vibrationPattern; | |||||
n.localOnly = optionalParamsThreeView.localOnlyButton.getToggleState(); | |||||
n.ongoing = optionalParamsThreeView.ongoingButton.getToggleState(); | |||||
n.timestampVisibility = Notification::TimestampVisibility (optionalParamsThreeView.timestampVisibilityComboBox.getSelectedItemIndex()); | |||||
if (optionalParamsThreeView.timeoutAfterComboBox.getSelectedItemIndex() > 0) | |||||
{ | |||||
auto index = optionalParamsThreeView.timeoutAfterComboBox.getSelectedItemIndex(); | |||||
n.timeoutAfterMs = index * 1000 + 4000; | |||||
} | |||||
} | |||||
void MainContentComponent::setupAccentColour() | |||||
{ | |||||
optionalParamsThreeView.accentColourSelector = new ColourSelector(); | |||||
optionalParamsThreeView.accentColourSelector->setName ("accent colour"); | |||||
optionalParamsThreeView.accentColourSelector->setCurrentColour (optionalParamsThreeView.accentColourButton.findColour (TextButton::buttonColourId)); | |||||
optionalParamsThreeView.accentColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||||
optionalParamsThreeView.accentColourSelector->setSize (200, 200); | |||||
optionalParamsThreeView.accentColourSelector->addComponentListener (this); | |||||
optionalParamsThreeView.accentColourSelector->addChangeListener (this); | |||||
CallOutBox::launchAsynchronously (optionalParamsThreeView.accentColourSelector, optionalParamsThreeView.accentColourButton.getScreenBounds(), nullptr); | |||||
} | |||||
void MainContentComponent::setupLedColour() | |||||
{ | |||||
optionalParamsThreeView.ledColourSelector = new ColourSelector(); | |||||
optionalParamsThreeView.ledColourSelector->setName ("led colour"); | |||||
optionalParamsThreeView.ledColourSelector->setCurrentColour (optionalParamsThreeView.ledColourButton.findColour (TextButton::buttonColourId)); | |||||
optionalParamsThreeView.ledColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||||
optionalParamsThreeView.ledColourSelector->setSize (200, 200); | |||||
optionalParamsThreeView.ledColourSelector->addComponentListener (this); | |||||
optionalParamsThreeView.ledColourSelector->addChangeListener (this); | |||||
CallOutBox::launchAsynchronously (optionalParamsThreeView.ledColourSelector, optionalParamsThreeView.accentColourButton.getScreenBounds(), nullptr); | |||||
} | |||||
void MainContentComponent::changeListenerCallback (ChangeBroadcaster* source) | |||||
{ | |||||
if (source == optionalParamsThreeView.accentColourSelector) | |||||
{ | |||||
Colour c = optionalParamsThreeView.accentColourSelector->getCurrentColour(); | |||||
optionalParamsThreeView.accentColourButton.setColour (TextButton::buttonColourId, c); | |||||
} | |||||
else if (source == optionalParamsThreeView.ledColourSelector) | |||||
{ | |||||
Colour c = optionalParamsThreeView.ledColourSelector->getCurrentColour(); | |||||
optionalParamsThreeView.ledColourButton.setColour (TextButton::buttonColourId, c); | |||||
} | |||||
} | |||||
void MainContentComponent::componentBeingDeleted (Component& component) | |||||
{ | |||||
if (&component == optionalParamsThreeView.accentColourSelector) | |||||
optionalParamsThreeView.accentColourSelector = nullptr; | |||||
else if (&component == optionalParamsThreeView.ledColourSelector) | |||||
optionalParamsThreeView.ledColourSelector = nullptr; | |||||
} | |||||
void MainContentComponent::handleNotification (bool isLocalNotification, const PushNotifications::Notification& n) | |||||
{ | |||||
ignoreUnused (isLocalNotification); | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Received notification", | |||||
"ID: " + n.identifier | |||||
+ ", title: " + n.title | |||||
+ ", body: " + n.body); | |||||
} | |||||
void MainContentComponent::handleNotificationAction (bool isLocalNotification, | |||||
const PushNotifications::Notification& n, | |||||
const String& actionIdentifier, | |||||
const String& optionalResponse) | |||||
{ | |||||
ignoreUnused (isLocalNotification); | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Received notification action", | |||||
"ID: " + n.identifier | |||||
+ ", title: " + n.title | |||||
+ ", body: " + n.body | |||||
+ ", action: " + actionIdentifier | |||||
+ ", optionalResponse: " + optionalResponse); | |||||
PushNotifications::getInstance()->removeDeliveredNotification (n.identifier); | |||||
} | |||||
void MainContentComponent::localNotificationDismissedByUser (const PushNotifications::Notification& n) | |||||
{ | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Notification dismissed by a user", | |||||
"ID: " + n.identifier | |||||
+ ", title: " + n.title | |||||
+ ", body: " + n.body); | |||||
} | |||||
void MainContentComponent::getDeliveredNotifications() | |||||
{ | |||||
PushNotifications::getInstance()->getDeliveredNotifications(); | |||||
} | |||||
void MainContentComponent::deliveredNotificationsListReceived (const Array<PushNotifications::Notification>& notifs) | |||||
{ | |||||
String text = "Received notifications: "; | |||||
for (const auto& n : notifs) | |||||
text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Received notification list", text); | |||||
} | |||||
void MainContentComponent::pendingLocalNotificationsListReceived (const Array<PushNotifications::Notification>& notifs) | |||||
{ | |||||
String text = "Pending notifications: "; | |||||
for (const auto& n : notifs) | |||||
text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Pending notification list", text); | |||||
} | |||||
void MainContentComponent::deviceTokenRefreshed (const String& token) | |||||
{ | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Device token refreshed", | |||||
token); | |||||
} | |||||
#if JUCE_ANDROID | |||||
void MainContentComponent::remoteNotificationsDeleted() | |||||
{ | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Remote notifications deleted", | |||||
"Some of the pending messages were removed!"); | |||||
} | |||||
void MainContentComponent::upstreamMessageSent (const String& messageId) | |||||
{ | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Upstream message sent", | |||||
"Message id: " + messageId); | |||||
} | |||||
void MainContentComponent::upstreamMessageSendingError (const String& messageId, const String& error) | |||||
{ | |||||
NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
"Upstream message sending error", | |||||
"Message id: " + messageId | |||||
+ "\nerror: " + error); | |||||
} | |||||
Array<PushNotifications::Channel> MainContentComponent::getAndroidChannels() | |||||
{ | |||||
using Channel = PushNotifications::Channel; | |||||
Channel ch1, ch2, ch3; | |||||
ch1.identifier = "1"; | |||||
ch1.name = "HighImportance"; | |||||
ch1.importance = PushNotifications::Channel::max; | |||||
ch1.lockScreenAppearance = PushNotifications::Notification::showCompletely; | |||||
ch1.description = "High Priority Channel for important stuff"; | |||||
ch1.groupId = "demoGroup"; | |||||
ch1.ledColour = Colours::red; | |||||
ch1.bypassDoNotDisturb = true; | |||||
ch1.canShowBadge = true; | |||||
ch1.enableLights = true; | |||||
ch1.enableVibration = true; | |||||
ch1.soundToPlay = URL ("demonstrative"); | |||||
ch1.vibrationPattern = { 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200 }; | |||||
ch2.identifier = "2"; | |||||
ch2.name = "MediumImportance"; | |||||
ch2.importance = PushNotifications::Channel::normal; | |||||
ch2.lockScreenAppearance = PushNotifications::Notification::showPartially; | |||||
ch2.description = "Medium Priority Channel for standard stuff"; | |||||
ch2.groupId = "demoGroup"; | |||||
ch2.ledColour = Colours::yellow; | |||||
ch2.canShowBadge = true; | |||||
ch2.enableLights = true; | |||||
ch2.enableVibration = true; | |||||
ch2.soundToPlay = URL ("default_os_sound"); | |||||
ch2.vibrationPattern = { 1000, 1000 }; | |||||
ch3.identifier = "3"; | |||||
ch3.name = "LowImportance"; | |||||
ch3.importance = PushNotifications::Channel::min; | |||||
ch3.lockScreenAppearance = PushNotifications::Notification::dontShow; | |||||
ch3.description = "Low Priority Channel for silly stuff"; | |||||
ch3.groupId = "demoGroup"; | |||||
return { ch1, ch2, ch3 }; | |||||
} | |||||
#elif JUCE_IOS | |||||
PushNotifications::Settings MainContentComponent::getIosSettings() | |||||
{ | |||||
using Action = PushNotifications::Settings::Action; | |||||
using Category = PushNotifications::Settings::Category; | |||||
Action okAction; | |||||
okAction.identifier = "okAction"; | |||||
okAction.title = "OK!"; | |||||
okAction.style = Action::button; | |||||
okAction.triggerInBackground = true; | |||||
Action cancelAction; | |||||
cancelAction.identifier = "cancelAction"; | |||||
cancelAction.title = "Cancel"; | |||||
cancelAction.style = Action::button; | |||||
cancelAction.triggerInBackground = true; | |||||
cancelAction.destructive = true; | |||||
Action textAction; | |||||
textAction.identifier = "textAction"; | |||||
textAction.title = "Enter text"; | |||||
textAction.style = Action::text; | |||||
textAction.triggerInBackground = true; | |||||
textAction.destructive = false; | |||||
textAction.textInputButtonText = "Ok"; | |||||
textAction.textInputPlaceholder = "Enter text..."; | |||||
Category okCategory; | |||||
okCategory.identifier = "okCategory"; | |||||
okCategory.actions = { okAction }; | |||||
Category okCancelCategory; | |||||
okCancelCategory.identifier = "okCancelCategory"; | |||||
okCancelCategory.actions = { okAction, cancelAction }; | |||||
Category textCategory; | |||||
textCategory.identifier = "textCategory"; | |||||
textCategory.actions = { textAction }; | |||||
textCategory.sendDismissAction = true; | |||||
PushNotifications::Settings settings; | |||||
settings.allowAlert = true; | |||||
settings.allowBadge = true; | |||||
settings.allowSound = true; | |||||
settings.categories = { okCategory, okCancelCategory, textCategory }; | |||||
return settings; | |||||
} | |||||
#endif |
@@ -0,0 +1,658 @@ | |||||
/* | |||||
============================================================================== | |||||
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. | |||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
27th April 2017). | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
Or: You may also use this code under the terms of the GPL v3 (see | |||||
www.gnu.org/licenses). | |||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||||
DISCLAIMED. | |||||
============================================================================== | |||||
*/ | |||||
#pragma once | |||||
#include "../JuceLibraryCode/JuceHeader.h" | |||||
//============================================================================== | |||||
class MainContentComponent : public Component, | |||||
private Button::Listener, | |||||
private ComboBox::Listener, | |||||
private ChangeListener, | |||||
private ComponentListener, | |||||
private PushNotifications::Listener | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
MainContentComponent(); | |||||
~MainContentComponent(); | |||||
void paint (Graphics&) override; | |||||
void resized() override; | |||||
private: | |||||
void buttonClicked (Button*) override; | |||||
void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override; | |||||
void sendLocalNotification(); | |||||
void fillRequiredParams (PushNotifications::Notification& n); | |||||
void fillOptionalParamsOne (PushNotifications::Notification& n); | |||||
void fillOptionalParamsTwo (PushNotifications::Notification& n); | |||||
void fillOptionalParamsThree (PushNotifications::Notification& n); | |||||
void setupAccentColour(); | |||||
void setupLedColour(); | |||||
void getDeliveredNotifications(); | |||||
void changeListenerCallback (ChangeBroadcaster* source) override; | |||||
void componentBeingDeleted (Component& component) override; | |||||
void handleNotification (bool isLocalNotification, const PushNotifications::Notification& n) override; | |||||
void handleNotificationAction (bool isLocalNotification, | |||||
const PushNotifications::Notification& n, | |||||
const juce::String& actionIdentifier, | |||||
const juce::String& optionalResponse) override; | |||||
void localNotificationDismissedByUser (const PushNotifications::Notification& n) override; | |||||
void deliveredNotificationsListReceived (const Array<PushNotifications::Notification>&) override; | |||||
void pendingLocalNotificationsListReceived (const Array<PushNotifications::Notification>&) override; | |||||
void deviceTokenRefreshed (const String& token) override; | |||||
#if JUCE_ANDROID | |||||
void remoteNotificationsDeleted() override; | |||||
void upstreamMessageSent (const String& messageId) override; | |||||
void upstreamMessageSendingError (const String& messageId, const String& error) override; | |||||
static Array<PushNotifications::Channel> getAndroidChannels(); | |||||
#elif JUCE_IOS | |||||
static PushNotifications::Settings getIosSettings(); | |||||
#endif | |||||
struct RequiredParamsView : public Component | |||||
{ | |||||
RequiredParamsView() | |||||
{ | |||||
addAndMakeVisible (identifierLabel); | |||||
addAndMakeVisible (identifierEditor); | |||||
addAndMakeVisible (titleLabel); | |||||
addAndMakeVisible (titleEditor); | |||||
addAndMakeVisible (bodyLabel); | |||||
addAndMakeVisible (bodyEditor); | |||||
#if JUCE_IOS | |||||
addAndMakeVisible (categoryLabel); | |||||
addAndMakeVisible (categoryComboBox); | |||||
categories.add ("okCategory"); | |||||
categories.add ("okCancelCategory"); | |||||
categories.add ("textCategory"); | |||||
for (const auto& c : categories) | |||||
categoryComboBox.addItem (c, categoryComboBox.getNumItems() + 1); | |||||
categoryComboBox.setSelectedItemIndex (0); | |||||
#elif JUCE_ANDROID | |||||
#if __ANDROID_API__ >= 26 | |||||
addAndMakeVisible (channelIdLabel); | |||||
addAndMakeVisible (channelIdComboBox); | |||||
for (int i = 1; i <= 3; ++i) | |||||
channelIdComboBox.addItem (String (i), i); | |||||
channelIdComboBox.setSelectedItemIndex (0); | |||||
#endif | |||||
addAndMakeVisible (iconLabel); | |||||
addAndMakeVisible (iconComboBox); | |||||
for (int i = 0; i < 5; ++i) | |||||
iconComboBox.addItem ("icon" + String (i + 1), i + 1); | |||||
iconComboBox.setSelectedItemIndex (0); | |||||
#endif | |||||
// For now, to be able to dismiss mobile keyboard. | |||||
setWantsKeyboardFocus (true); | |||||
} | |||||
void resized() override | |||||
{ | |||||
const int labelColumnWidth = getWidth() / 3; | |||||
#if JUCE_ANDROID && __ANDROID_API__ >= 26 | |||||
const int rowHeight = getHeight() / 8; | |||||
#else | |||||
const int rowHeight = getHeight() / 7; | |||||
#endif | |||||
auto layoutRow = [labelColumnWidth] (Rectangle<int> rowBounds, Component& label, Component& editor) | |||||
{ | |||||
label .setBounds (rowBounds.removeFromLeft (labelColumnWidth)); | |||||
editor.setBounds (rowBounds); | |||||
}; | |||||
auto bounds = getLocalBounds(); | |||||
layoutRow (bounds.removeFromTop (rowHeight), identifierLabel, identifierEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), titleLabel, titleEditor); | |||||
layoutRow (bounds.removeFromTop (4 * rowHeight), bodyLabel, bodyEditor); | |||||
#if JUCE_IOS | |||||
layoutRow (bounds.removeFromTop (rowHeight), categoryLabel, categoryComboBox); | |||||
#elif JUCE_ANDROID | |||||
#if __ANDROID_API__ >= 26 | |||||
layoutRow (bounds.removeFromTop (rowHeight), channelIdLabel, channelIdComboBox); | |||||
#endif | |||||
layoutRow (bounds.removeFromTop (rowHeight), iconLabel, iconComboBox); | |||||
#endif | |||||
} | |||||
Label identifierLabel { "identifierLabel", "Identifier" }; | |||||
TextEditor identifierEditor; | |||||
Label titleLabel { "titleLabel", "Title" }; | |||||
TextEditor titleEditor; | |||||
Label bodyLabel { "bodyLabel", "Body" }; | |||||
TextEditor bodyEditor; | |||||
#if JUCE_IOS | |||||
StringArray categories; | |||||
Label categoryLabel { "categoryLabel", "Category" }; | |||||
ComboBox categoryComboBox; | |||||
#elif JUCE_ANDROID | |||||
Label channelIdLabel { "channelIdLabel", "Channel ID" }; | |||||
ComboBox channelIdComboBox; | |||||
Label iconLabel { "iconLabel", "Icon" }; | |||||
ComboBox iconComboBox; | |||||
#endif | |||||
}; | |||||
struct OptionalParamsOneView : public Component | |||||
{ | |||||
OptionalParamsOneView() | |||||
{ | |||||
addAndMakeVisible (subtitleLabel); | |||||
addAndMakeVisible (subtitleEditor); | |||||
addAndMakeVisible (badgeNumberLabel); | |||||
addAndMakeVisible (badgeNumberComboBox); | |||||
addAndMakeVisible (soundToPlayLabel); | |||||
addAndMakeVisible (soundToPlayComboBox); | |||||
addAndMakeVisible (propertiesLabel); | |||||
addAndMakeVisible (propertiesEditor); | |||||
#if JUCE_IOS | |||||
addAndMakeVisible (fireInLabel); | |||||
addAndMakeVisible (fireInComboBox); | |||||
addAndMakeVisible (repeatLabel); | |||||
addAndMakeVisible (repeatButton); | |||||
fireInComboBox.addItem ("Now", 1); | |||||
for (int i = 1; i < 11; ++i) | |||||
fireInComboBox.addItem (String (10 * i) + "seconds", i + 1); | |||||
fireInComboBox.setSelectedItemIndex (0); | |||||
#elif JUCE_ANDROID | |||||
addAndMakeVisible (largeIconLabel); | |||||
addAndMakeVisible (largeIconComboBox); | |||||
addAndMakeVisible (badgeIconLabel); | |||||
addAndMakeVisible (badgeIconComboBox); | |||||
addAndMakeVisible (tickerTextLabel); | |||||
addAndMakeVisible (tickerTextEditor); | |||||
addAndMakeVisible (autoCancelLabel); | |||||
addAndMakeVisible (autoCancelButton); | |||||
addAndMakeVisible (alertOnlyOnceLabel); | |||||
addAndMakeVisible (alertOnlyOnceButton); | |||||
addAndMakeVisible (actionsLabel); | |||||
addAndMakeVisible (actionsComboBox); | |||||
largeIconComboBox.addItem ("none", 1); | |||||
for (int i = 1; i < 5; ++i) | |||||
largeIconComboBox.addItem ("icon" + String (i), i + 1); | |||||
largeIconComboBox.setSelectedItemIndex (0); | |||||
badgeIconComboBox.addItem ("none", 1); | |||||
badgeIconComboBox.addItem ("small", 2); | |||||
badgeIconComboBox.addItem ("large", 3); | |||||
badgeIconComboBox.setSelectedItemIndex (2); | |||||
actionsComboBox.addItem ("none", 1); | |||||
actionsComboBox.addItem ("ok-cancel", 2); | |||||
actionsComboBox.addItem ("ok-cancel-icons", 3); | |||||
actionsComboBox.addItem ("text-input", 4); | |||||
actionsComboBox.addItem ("text-input-limited_responses", 5); | |||||
actionsComboBox.setSelectedItemIndex (0); | |||||
#endif | |||||
for (int i = 0; i < 7; ++i) | |||||
badgeNumberComboBox.addItem (String (i), i + 1); | |||||
badgeNumberComboBox.setSelectedItemIndex (0); | |||||
#if JUCE_IOS | |||||
String prefix = "sounds/"; | |||||
String extension = ".caf"; | |||||
#else | |||||
String prefix; | |||||
String extension; | |||||
#endif | |||||
soundToPlayComboBox.addItem ("none", 1); | |||||
soundToPlayComboBox.addItem ("default_os_sound", 2); | |||||
soundToPlayComboBox.addItem (prefix + "demonstrative" + extension, 3); | |||||
soundToPlayComboBox.addItem (prefix + "isntit" + extension, 4); | |||||
soundToPlayComboBox.addItem (prefix + "jinglebellssms" + extension, 5); | |||||
soundToPlayComboBox.addItem (prefix + "served" + extension, 6); | |||||
soundToPlayComboBox.addItem (prefix + "solemn" + extension, 7); | |||||
soundToPlayComboBox.setSelectedItemIndex (1); | |||||
// For now, to be able to dismiss mobile keyboard. | |||||
setWantsKeyboardFocus (true); | |||||
} | |||||
void resized() override | |||||
{ | |||||
const int labelColumnWidth = getWidth() / 3; | |||||
#if JUCE_ANDROID | |||||
const int rowHeight = getHeight() / 12; | |||||
#else | |||||
const int rowHeight = getHeight() / 8; | |||||
#endif | |||||
auto layoutRow = [labelColumnWidth] (Rectangle<int> rowBounds, Component& label, Component& editor) | |||||
{ | |||||
label .setBounds (rowBounds.removeFromLeft (labelColumnWidth)); | |||||
editor.setBounds (rowBounds); | |||||
}; | |||||
auto bounds = getLocalBounds(); | |||||
layoutRow (bounds.removeFromTop (rowHeight), subtitleLabel, subtitleEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), badgeNumberLabel, badgeNumberComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), soundToPlayLabel, soundToPlayComboBox); | |||||
layoutRow (bounds.removeFromTop (3 * rowHeight), propertiesLabel, propertiesEditor); | |||||
#if JUCE_IOS | |||||
layoutRow (bounds.removeFromTop (rowHeight), fireInLabel, fireInComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), repeatLabel, repeatButton); | |||||
#elif JUCE_ANDROID | |||||
layoutRow (bounds.removeFromTop (rowHeight), largeIconLabel, largeIconComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), badgeIconLabel, badgeIconComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), tickerTextLabel, tickerTextEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), autoCancelLabel, autoCancelButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), alertOnlyOnceLabel, alertOnlyOnceButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), actionsLabel, actionsComboBox); | |||||
#endif | |||||
} | |||||
Label subtitleLabel { "subtitleLabel", "Subtitle" }; | |||||
TextEditor subtitleEditor; | |||||
Label badgeNumberLabel { "badgeNumberLabel", "BadgeNumber" }; | |||||
ComboBox badgeNumberComboBox; | |||||
Label soundToPlayLabel { "soundToPlayLabel", "SoundToPlay" }; | |||||
ComboBox soundToPlayComboBox; | |||||
Label propertiesLabel { "propertiesLabel", "Properties" }; | |||||
TextEditor propertiesEditor; | |||||
#if JUCE_IOS | |||||
Label fireInLabel { "fireInLabel", "Fire in" }; | |||||
ComboBox fireInComboBox; | |||||
Label repeatLabel { "repeatLabel", "Repeat" }; | |||||
ToggleButton repeatButton; | |||||
#elif JUCE_ANDROID | |||||
Label largeIconLabel { "largeIconLabel", "Large Icon" }; | |||||
ComboBox largeIconComboBox; | |||||
Label badgeIconLabel { "badgeIconLabel", "Badge Icon" }; | |||||
ComboBox badgeIconComboBox; | |||||
Label tickerTextLabel { "tickerTextLabel", "Ticker Text" }; | |||||
TextEditor tickerTextEditor; | |||||
Label autoCancelLabel { "autoCancelLabel", "AutoCancel" }; | |||||
ToggleButton autoCancelButton; | |||||
Label alertOnlyOnceLabel { "alertOnlyOnceLabel", "AlertOnlyOnce" }; | |||||
ToggleButton alertOnlyOnceButton; | |||||
Label actionsLabel { "actionsLabel", "Actions" }; | |||||
ComboBox actionsComboBox; | |||||
#endif | |||||
}; | |||||
struct OptionalParamsTwoView : public Component | |||||
{ | |||||
OptionalParamsTwoView() | |||||
{ | |||||
addAndMakeVisible (progressMaxLabel); | |||||
addAndMakeVisible (progressMaxComboBox); | |||||
addAndMakeVisible (progressCurrentLabel); | |||||
addAndMakeVisible (progressCurrentComboBox); | |||||
addAndMakeVisible (progressIndeterminateLabel); | |||||
addAndMakeVisible (progressIndeterminateButton); | |||||
addAndMakeVisible (categoryLabel); | |||||
addAndMakeVisible (categoryComboBox); | |||||
addAndMakeVisible (priorityLabel); | |||||
addAndMakeVisible (priorityComboBox); | |||||
addAndMakeVisible (personLabel); | |||||
addAndMakeVisible (personEditor); | |||||
addAndMakeVisible (lockScreenVisibilityLabel); | |||||
addAndMakeVisible (lockScreenVisibilityComboBox); | |||||
addAndMakeVisible (groupIdLabel); | |||||
addAndMakeVisible (groupIdEditor); | |||||
addAndMakeVisible (sortKeyLabel); | |||||
addAndMakeVisible (sortKeyEditor); | |||||
addAndMakeVisible (groupSummaryLabel); | |||||
addAndMakeVisible (groupSummaryButton); | |||||
addAndMakeVisible (groupAlertBehaviourLabel); | |||||
addAndMakeVisible (groupAlertBehaviourComboBox); | |||||
for (int i = 0; i < 11; ++i) | |||||
{ | |||||
progressMaxComboBox .addItem (String (i * 10) + "%", i + 1); | |||||
progressCurrentComboBox.addItem (String (i * 10) + "%", i + 1); | |||||
} | |||||
progressMaxComboBox .setSelectedItemIndex (0); | |||||
progressCurrentComboBox.setSelectedItemIndex (0); | |||||
categoryComboBox.addItem ("unspecified", 1); | |||||
categoryComboBox.addItem ("alarm", 2); | |||||
categoryComboBox.addItem ("call", 3); | |||||
categoryComboBox.addItem ("email", 4); | |||||
categoryComboBox.addItem ("error", 5); | |||||
categoryComboBox.addItem ("event", 6); | |||||
categoryComboBox.addItem ("message", 7); | |||||
categoryComboBox.addItem ("progress", 8); | |||||
categoryComboBox.addItem ("promo", 9); | |||||
categoryComboBox.addItem ("recommendation", 10); | |||||
categoryComboBox.addItem ("reminder", 11); | |||||
categoryComboBox.addItem ("service", 12); | |||||
categoryComboBox.addItem ("social", 13); | |||||
categoryComboBox.addItem ("status", 14); | |||||
categoryComboBox.addItem ("system", 15); | |||||
categoryComboBox.addItem ("transport", 16); | |||||
categoryComboBox.setSelectedItemIndex (0); | |||||
for (int i = -2; i < 3; ++i) | |||||
priorityComboBox.addItem (String (i), i + 3); | |||||
priorityComboBox.setSelectedItemIndex (2); | |||||
lockScreenVisibilityComboBox.addItem ("don't show", 1); | |||||
lockScreenVisibilityComboBox.addItem ("show partially", 2); | |||||
lockScreenVisibilityComboBox.addItem ("show completely", 3); | |||||
lockScreenVisibilityComboBox.setSelectedItemIndex (1); | |||||
groupAlertBehaviourComboBox.addItem ("alert all", 1); | |||||
groupAlertBehaviourComboBox.addItem ("alert summary", 2); | |||||
groupAlertBehaviourComboBox.addItem ("alert children", 3); | |||||
groupAlertBehaviourComboBox.setSelectedItemIndex (0); | |||||
// For now, to be able to dismiss mobile keyboard. | |||||
setWantsKeyboardFocus (true); | |||||
} | |||||
void resized() override | |||||
{ | |||||
const int labelColumnWidth = getWidth() / 3; | |||||
const int rowHeight = getHeight() / 11; | |||||
auto layoutRow = [labelColumnWidth] (Rectangle<int> rowBounds, Component& label, Component& editor) | |||||
{ | |||||
label .setBounds (rowBounds.removeFromLeft (labelColumnWidth)); | |||||
editor.setBounds (rowBounds); | |||||
}; | |||||
auto bounds = getLocalBounds(); | |||||
layoutRow (bounds.removeFromTop (rowHeight), progressMaxLabel, progressMaxComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), progressCurrentLabel, progressCurrentComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), progressIndeterminateLabel, progressIndeterminateButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), categoryLabel, categoryComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), priorityLabel, priorityComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), personLabel, personEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), lockScreenVisibilityLabel, lockScreenVisibilityComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), groupIdLabel, groupIdEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), sortKeyLabel, sortKeyEditor); | |||||
layoutRow (bounds.removeFromTop (rowHeight), groupSummaryLabel, groupSummaryButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), groupAlertBehaviourLabel, groupAlertBehaviourComboBox); | |||||
} | |||||
Label progressMaxLabel { "progressMaxLabel", "ProgressMax" }; | |||||
ComboBox progressMaxComboBox; | |||||
Label progressCurrentLabel { "progressCurrentLabel", "ProgressCurrent" }; | |||||
ComboBox progressCurrentComboBox; | |||||
Label progressIndeterminateLabel { "progressIndeterminateLabel", "ProgressIndeterminate" }; | |||||
ToggleButton progressIndeterminateButton; | |||||
Label categoryLabel { "categoryLabel", "Category" }; | |||||
ComboBox categoryComboBox; | |||||
Label priorityLabel { "priorityLabel", "Priority" }; | |||||
ComboBox priorityComboBox; | |||||
Label personLabel { "personLabel", "Person" }; | |||||
TextEditor personEditor; | |||||
Label lockScreenVisibilityLabel { "lockScreenVisibilityLabel", "LockScreenVisibility" }; | |||||
ComboBox lockScreenVisibilityComboBox; | |||||
Label groupIdLabel { "groupIdLabel", "GroupID" }; | |||||
TextEditor groupIdEditor; | |||||
Label sortKeyLabel { "sortKeyLabel", "SortKey" }; | |||||
TextEditor sortKeyEditor; | |||||
Label groupSummaryLabel { "groupSummaryLabel", "GroupSummary" }; | |||||
ToggleButton groupSummaryButton; | |||||
Label groupAlertBehaviourLabel { "groupAlertBehaviourLabel", "GroupAlertBehaviour" }; | |||||
ComboBox groupAlertBehaviourComboBox; | |||||
}; | |||||
struct OptionalParamsThreeView : public Component | |||||
{ | |||||
OptionalParamsThreeView() | |||||
{ | |||||
addAndMakeVisible (accentColourLabel); | |||||
addAndMakeVisible (accentColourButton); | |||||
addAndMakeVisible (ledColourLabel); | |||||
addAndMakeVisible (ledColourButton); | |||||
addAndMakeVisible (ledMsToBeOnLabel); | |||||
addAndMakeVisible (ledMsToBeOnComboBox); | |||||
addAndMakeVisible (ledMsToBeOffLabel); | |||||
addAndMakeVisible (ledMsToBeOffComboBox); | |||||
addAndMakeVisible (vibratorMsToBeOnLabel); | |||||
addAndMakeVisible (vibratorMsToBeOnComboBox); | |||||
addAndMakeVisible (vibratorMsToBeOffLabel); | |||||
addAndMakeVisible (vibratorMsToBeOffComboBox); | |||||
addAndMakeVisible (localOnlyLabel); | |||||
addAndMakeVisible (localOnlyButton); | |||||
addAndMakeVisible (ongoingLabel); | |||||
addAndMakeVisible (ongoingButton); | |||||
addAndMakeVisible (timestampVisibilityLabel); | |||||
addAndMakeVisible (timestampVisibilityComboBox); | |||||
addAndMakeVisible (timeoutAfterLabel); | |||||
addAndMakeVisible (timeoutAfterComboBox); | |||||
timeoutAfterComboBox.addItem ("No timeout", 1); | |||||
for (int i = 0; i < 10; ++i) | |||||
{ | |||||
ledMsToBeOnComboBox .addItem (String (i * 200) + "ms", i + 1); | |||||
ledMsToBeOffComboBox .addItem (String (i * 200) + "ms", i + 1); | |||||
vibratorMsToBeOnComboBox .addItem (String (i * 500) + "ms", i + 1); | |||||
vibratorMsToBeOffComboBox.addItem (String (i * 500) + "ms", i + 1); | |||||
timeoutAfterComboBox.addItem (String (5000 + 1000 * i) + "ms", i + 2); | |||||
} | |||||
ledMsToBeOnComboBox .setSelectedItemIndex (5); | |||||
ledMsToBeOffComboBox .setSelectedItemIndex (5); | |||||
vibratorMsToBeOnComboBox .setSelectedItemIndex (0); | |||||
vibratorMsToBeOffComboBox.setSelectedItemIndex (0); | |||||
timeoutAfterComboBox.setSelectedItemIndex (0); | |||||
timestampVisibilityComboBox.addItem ("off", 1); | |||||
timestampVisibilityComboBox.addItem ("on", 2); | |||||
timestampVisibilityComboBox.addItem ("chronometer", 3); | |||||
timestampVisibilityComboBox.addItem ("count down", 4); | |||||
timestampVisibilityComboBox.setSelectedItemIndex (1); | |||||
// For now, to be able to dismiss mobile keyboard. | |||||
setWantsKeyboardFocus (true); | |||||
} | |||||
void resized() override | |||||
{ | |||||
const int labelColumnWidth = getWidth() / 3; | |||||
const int rowHeight = getHeight() / 10; | |||||
auto layoutRow = [labelColumnWidth] (Rectangle<int> rowBounds, Component& label, Component& editor) | |||||
{ | |||||
label .setBounds (rowBounds.removeFromLeft (labelColumnWidth)); | |||||
editor.setBounds (rowBounds); | |||||
}; | |||||
auto bounds = getLocalBounds(); | |||||
layoutRow (bounds.removeFromTop (rowHeight), accentColourLabel, accentColourButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), ledColourLabel, ledColourButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), ledMsToBeOnLabel, ledMsToBeOnComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), ledMsToBeOffLabel, ledMsToBeOffComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), vibratorMsToBeOnLabel, vibratorMsToBeOnComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), vibratorMsToBeOffLabel, vibratorMsToBeOffComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), localOnlyLabel, localOnlyButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), ongoingLabel, ongoingButton); | |||||
layoutRow (bounds.removeFromTop (rowHeight), timestampVisibilityLabel, timestampVisibilityComboBox); | |||||
layoutRow (bounds.removeFromTop (rowHeight), timeoutAfterLabel, timeoutAfterComboBox); | |||||
} | |||||
Label accentColourLabel { "accentColourLabel", "AccentColour" }; | |||||
TextButton accentColourButton; | |||||
Label ledColourLabel { "ledColourLabel", "LedColour" }; | |||||
TextButton ledColourButton; | |||||
Label ledMsToBeOnLabel { "ledMsToBeOnLabel", "LedMsToBeOn" }; | |||||
ComboBox ledMsToBeOnComboBox; | |||||
Label ledMsToBeOffLabel { "ledMsToBeOffLabel", "LedMsToBeOff" }; | |||||
ComboBox ledMsToBeOffComboBox; | |||||
Label vibratorMsToBeOnLabel { "vibratorMsToBeOnLabel", "VibrationMsToBeOn" }; | |||||
ComboBox vibratorMsToBeOnComboBox; | |||||
Label vibratorMsToBeOffLabel { "vibratorMsToBeOffLabel", "VibrationMsToBeOff" }; | |||||
ComboBox vibratorMsToBeOffComboBox; | |||||
Label localOnlyLabel { "localOnlyLabel", "LocalOnly" }; | |||||
ToggleButton localOnlyButton; | |||||
Label ongoingLabel { "ongoingLabel", "Ongoing" }; | |||||
ToggleButton ongoingButton; | |||||
Label timestampVisibilityLabel { "timestampVisibilityLabel", "TimestampMode" }; | |||||
ComboBox timestampVisibilityComboBox; | |||||
Label timeoutAfterLabel { "timeoutAfterLabel", "Timeout After Ms" }; | |||||
ComboBox timeoutAfterComboBox; | |||||
ColourSelector* accentColourSelector = nullptr; | |||||
ColourSelector* ledColourSelector = nullptr; | |||||
}; | |||||
struct AuxActionsView : public Component | |||||
{ | |||||
AuxActionsView() | |||||
{ | |||||
addAndMakeVisible (getDeliveredNotificationsButton); | |||||
addAndMakeVisible (removeDeliveredNotifWithIdButton); | |||||
addAndMakeVisible (deliveredNotifIdentifier); | |||||
addAndMakeVisible (removeAllDeliveredNotifsButton); | |||||
#if JUCE_IOS | |||||
addAndMakeVisible (getPendingNotificationsButton); | |||||
addAndMakeVisible (removePendingNotifWithIdButton); | |||||
addAndMakeVisible (pendingNotifIdentifier); | |||||
addAndMakeVisible (removeAllPendingNotifsButton); | |||||
#endif | |||||
// For now, to be able to dismiss mobile keyboard. | |||||
setWantsKeyboardFocus (true); | |||||
} | |||||
void resized() override | |||||
{ | |||||
const int columnWidth = getWidth(); | |||||
const int rowHeight = getHeight() / 6; | |||||
auto bounds = getLocalBounds(); | |||||
getDeliveredNotificationsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||||
auto rowBounds = bounds.removeFromTop (rowHeight); | |||||
removeDeliveredNotifWithIdButton.setBounds (rowBounds.removeFromLeft (columnWidth / 2)); | |||||
deliveredNotifIdentifier .setBounds (rowBounds); | |||||
removeAllDeliveredNotifsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||||
#if JUCE_IOS | |||||
getPendingNotificationsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||||
rowBounds = bounds.removeFromTop (rowHeight); | |||||
removePendingNotifWithIdButton.setBounds (rowBounds.removeFromLeft (columnWidth / 2)); | |||||
pendingNotifIdentifier .setBounds (rowBounds); | |||||
removeAllPendingNotifsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||||
#endif | |||||
} | |||||
TextButton getDeliveredNotificationsButton { "Get Delivered Notifications" }; | |||||
TextButton removeDeliveredNotifWithIdButton { "Remove Delivered Notif With ID:" }; | |||||
TextEditor deliveredNotifIdentifier; | |||||
TextButton removeAllDeliveredNotifsButton { "Remove All Delivered Notifs" }; | |||||
#if JUCE_IOS | |||||
TextButton getPendingNotificationsButton { "Get Pending Notifications" }; | |||||
TextButton removePendingNotifWithIdButton { "Remove Pending Notif With ID:" }; | |||||
TextEditor pendingNotifIdentifier; | |||||
TextButton removeAllPendingNotifsButton { "Remove All Pending Notifs" }; | |||||
#endif | |||||
}; | |||||
struct RemoteView : public Component | |||||
{ | |||||
RemoteView() | |||||
{ | |||||
addAndMakeVisible (getDeviceTokenButton); | |||||
#if JUCE_ANDROID | |||||
addAndMakeVisible (sendRemoteMessageButton); | |||||
addAndMakeVisible (subscribeToSportsButton); | |||||
addAndMakeVisible (unsubscribeFromSportsButton); | |||||
#endif | |||||
} | |||||
void resized() | |||||
{ | |||||
const int rowSize = getHeight () / 10; | |||||
auto bounds = getLocalBounds().reduced (getWidth() / 10, getHeight() / 10); | |||||
bounds.removeFromTop (2 * rowSize); | |||||
getDeviceTokenButton .setBounds (bounds.removeFromTop (rowSize)); | |||||
sendRemoteMessageButton .setBounds (bounds.removeFromTop (rowSize)); | |||||
subscribeToSportsButton .setBounds (bounds.removeFromTop (rowSize)); | |||||
unsubscribeFromSportsButton.setBounds (bounds.removeFromTop (rowSize)); | |||||
} | |||||
TextButton getDeviceTokenButton { "GetDeviceToken" }; | |||||
TextButton sendRemoteMessageButton { "SendRemoteMessage" }; | |||||
TextButton subscribeToSportsButton { "SubscribeToSports" }; | |||||
TextButton unsubscribeFromSportsButton { "UnsubscribeFromSports" }; | |||||
}; | |||||
Label headerLabel { "headerLabel", "Push Notifications Demo" }; | |||||
RequiredParamsView requiredParamsView; | |||||
OptionalParamsOneView optionalParamsOneView; | |||||
OptionalParamsTwoView optionalParamsTwoView; | |||||
OptionalParamsThreeView optionalParamsThreeView; | |||||
AuxActionsView auxActionsView; | |||||
TabbedComponent localNotificationsTabs { TabbedButtonBar::TabsAtTop }; | |||||
RemoteView remoteView; | |||||
TabbedComponent mainTabs { TabbedButtonBar::TabsAtTop }; | |||||
TextButton sendButton { "Send!" }; | |||||
Label notAvailableYetLabel { "notAvailableYetLabel", "Push Notifications feature is not available on this platform yet!" }; | |||||
//============================================================================== | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) | |||||
}; |
@@ -0,0 +1,42 @@ | |||||
{ | |||||
"project_info": { | |||||
"project_number": "3137221487", | |||||
"firebase_url": "https://pushnotificationsdemo-1c714.firebaseio.com", | |||||
"project_id": "pushnotificationsdemo-1c714", | |||||
"storage_bucket": "pushnotificationsdemo-1c714.appspot.com" | |||||
}, | |||||
"client": [ | |||||
{ | |||||
"client_info": { | |||||
"mobilesdk_app_id": "1:3137221487:android:8fdcd861a33b035c", | |||||
"android_client_info": { | |||||
"package_name": "com.juce.pushnotificationsdemo" | |||||
} | |||||
}, | |||||
"oauth_client": [ | |||||
{ | |||||
"client_id": "3137221487-uftk61ukltbi07dmejslgt0d6qnml0oo.apps.googleusercontent.com", | |||||
"client_type": 3 | |||||
} | |||||
], | |||||
"api_key": [ | |||||
{ | |||||
"current_key": "AIzaSyDPpqphjiEEYI3sJGptrebN5Z52GkOG4Wo" | |||||
} | |||||
], | |||||
"services": { | |||||
"analytics_service": { | |||||
"status": 1 | |||||
}, | |||||
"appinvite_service": { | |||||
"status": 1, | |||||
"other_platform_oauth_client": [] | |||||
}, | |||||
"ads_service": { | |||||
"status": 2 | |||||
} | |||||
} | |||||
} | |||||
], | |||||
"configuration_version": "1" | |||||
} |
@@ -107,9 +107,12 @@ public: | |||||
CachedValue<bool> androidInternetNeeded, androidMicNeeded, androidBluetoothNeeded, | CachedValue<bool> androidInternetNeeded, androidMicNeeded, androidBluetoothNeeded, | ||||
androidExternalReadPermission, androidExternalWritePermission, | androidExternalReadPermission, androidExternalWritePermission, | ||||
androidInAppBillingPermission; | |||||
androidInAppBillingPermission, androidVibratePermission; | |||||
CachedValue<String> androidOtherPermissions; | CachedValue<String> androidOtherPermissions; | ||||
CachedValue<bool> androidEnableRemoteNotifications; | |||||
CachedValue<String> androidRemoteNotificationsConfigFile; | |||||
CachedValue<String> androidKeyStore, androidKeyStorePass, androidKeyAlias, androidKeyAliasPass; | CachedValue<String> androidKeyStore, androidKeyStorePass, androidKeyAlias, androidKeyAliasPass; | ||||
CachedValue<String> gradleVersion, androidPluginVersion, gradleToolchain, buildToolsVersion; | CachedValue<String> gradleVersion, androidPluginVersion, gradleToolchain, buildToolsVersion; | ||||
@@ -135,7 +138,10 @@ public: | |||||
androidExternalReadPermission (settings, Ids::androidExternalReadNeeded, nullptr, true), | androidExternalReadPermission (settings, Ids::androidExternalReadNeeded, nullptr, true), | ||||
androidExternalWritePermission (settings, Ids::androidExternalWriteNeeded, nullptr, true), | androidExternalWritePermission (settings, Ids::androidExternalWriteNeeded, nullptr, true), | ||||
androidInAppBillingPermission (settings, Ids::androidInAppBilling, nullptr, false), | androidInAppBillingPermission (settings, Ids::androidInAppBilling, nullptr, false), | ||||
androidVibratePermission (settings, Ids::androidVibratePermissionNeeded, nullptr, false), | |||||
androidOtherPermissions (settings, Ids::androidOtherPermissions, nullptr), | androidOtherPermissions (settings, Ids::androidOtherPermissions, nullptr), | ||||
androidEnableRemoteNotifications (settings, Ids::androidEnableRemoteNotifications, nullptr, false), | |||||
androidRemoteNotificationsConfigFile (settings, Ids::androidRemoteNotificationsConfigFile, nullptr, ""), | |||||
androidKeyStore (settings, Ids::androidKeyStore, nullptr, "${user.home}/.android/debug.keystore"), | androidKeyStore (settings, Ids::androidKeyStore, nullptr, "${user.home}/.android/debug.keystore"), | ||||
androidKeyStorePass (settings, Ids::androidKeyStorePass, nullptr, "android"), | androidKeyStorePass (settings, Ids::androidKeyStorePass, nullptr, "android"), | ||||
androidKeyAlias (settings, Ids::androidKeyAlias, nullptr, "androiddebugkey"), | androidKeyAlias (settings, Ids::androidKeyAlias, nullptr, "androiddebugkey"), | ||||
@@ -327,6 +333,9 @@ protected: | |||||
Value getAdditionalXmlResourcesValue() { return getValue (Ids::androidAdditionalXmlValueResources); } | Value getAdditionalXmlResourcesValue() { return getValue (Ids::androidAdditionalXmlValueResources); } | ||||
String getAdditionalXmlResources() const { return config [Ids::androidAdditionalXmlValueResources]; } | String getAdditionalXmlResources() const { return config [Ids::androidAdditionalXmlValueResources]; } | ||||
Value getAdditionalRawResourcesValue() { return getValue (Ids::androidAdditionalRawValueResources); } | |||||
String getAdditionalRawResources() const { return config [Ids::androidAdditionalRawValueResources]; } | |||||
Value getCustomStringsXmlValue() { return getValue (Ids::androidCustomStringXmlElements); } | Value getCustomStringsXmlValue() { return getValue (Ids::androidCustomStringXmlElements); } | ||||
String getCustomStringsXml() const { return config [Ids::androidCustomStringXmlElements]; } | String getCustomStringsXml() const { return config [Ids::androidCustomStringXmlElements]; } | ||||
@@ -343,8 +352,14 @@ protected: | |||||
"Paths to additional \"value resource\" files in XML format that should be included in the app (one per line). " | "Paths to additional \"value resource\" files in XML format that should be included in the app (one per line). " | ||||
"If you have additional XML resources that should be treated as value resources, add them here."); | "If you have additional XML resources that should be treated as value resources, add them here."); | ||||
props.add (new TextPropertyComponent (getCustomStringsXmlValue(), "Custom string.xml elements", 8192, true), | |||||
"You can specify custom XML elements that will be added to string.xml as children of <resources> element."); | |||||
props.add (new TextPropertyComponent (getAdditionalRawResourcesValue(), "Extra Android Raw Resources", 2048, true), | |||||
"Paths to additional \"raw resource\" files that should be included in the app (one per line). " | |||||
"Resource file names must contain only lowercase a-z, 0-9 or underscore."); | |||||
props.add (new TextPropertyComponent (getCustomStringsXmlValue(), "Custom string resources", 8192, true), | |||||
"Custom XML resources that will be added to string.xml as children of <resources> element. " | |||||
"Example: \n<string name=\"value\">text</string>\n" | |||||
"<string name2=\"value2\">text2</string>\n"); | |||||
} | } | ||||
String getProductFlavourNameIdentifier() const | String getProductFlavourNameIdentifier() const | ||||
@@ -533,12 +548,24 @@ private: | |||||
mo << " }" << newLine; | mo << " }" << newLine; | ||||
mo << " dependencies {" << newLine; | mo << " dependencies {" << newLine; | ||||
mo << " classpath 'com.android.tools.build:gradle:" << androidPluginVersion.get() << "'" << newLine; | mo << " classpath 'com.android.tools.build:gradle:" << androidPluginVersion.get() << "'" << newLine; | ||||
if (androidEnableRemoteNotifications.get()) | |||||
mo << " classpath 'com.google.gms:google-services:3.1.0'" << newLine; | |||||
mo << " }" << newLine; | mo << " }" << newLine; | ||||
mo << "}" << newLine; | mo << "}" << newLine; | ||||
mo << "" << newLine; | mo << "" << newLine; | ||||
mo << "allprojects {" << newLine; | mo << "allprojects {" << newLine; | ||||
mo << " repositories {" << newLine; | mo << " repositories {" << newLine; | ||||
mo << " jcenter()" << newLine; | mo << " jcenter()" << newLine; | ||||
if (androidEnableRemoteNotifications.get()) | |||||
{ | |||||
mo << " maven {" << newLine; | |||||
mo << " url \"https://maven.google.com\"" << newLine; | |||||
mo << " }" << newLine; | |||||
} | |||||
mo << " }" << newLine; | mo << " }" << newLine; | ||||
mo << "}" << newLine; | mo << "}" << newLine; | ||||
@@ -568,6 +595,7 @@ private: | |||||
mo << getAndroidRepositories() << newLine; | mo << getAndroidRepositories() << newLine; | ||||
mo << getAndroidDependencies() << newLine; | mo << getAndroidDependencies() << newLine; | ||||
mo << getApplyPlugins() << newLine; | |||||
mo << "}" << newLine << newLine; | mo << "}" << newLine << newLine; | ||||
@@ -748,12 +776,28 @@ private: | |||||
juce::StringArray dependencies; | juce::StringArray dependencies; | ||||
dependencies.addLines (androidDependencies.get()); | dependencies.addLines (androidDependencies.get()); | ||||
mo << "dependencies {" << newLine; | |||||
mo << "dependencies {" << 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; | |||||
} | |||||
for (const auto& d : dependencies) | for (const auto& d : dependencies) | ||||
mo << " " << d << newLine; | mo << " " << d << newLine; | ||||
mo << "}" << newLine; | |||||
mo << "}" << newLine; | |||||
return mo.toString(); | |||||
} | |||||
String getApplyPlugins() const | |||||
{ | |||||
MemoryOutputStream mo; | |||||
if (androidEnableRemoteNotifications.get()) | |||||
mo << "apply plugin: 'com.google.gms.google-services'" << newLine; | |||||
return mo.toString(); | return mo.toString(); | ||||
} | } | ||||
@@ -838,9 +882,19 @@ private: | |||||
props.add (new BooleanPropertyComponent (androidInAppBillingPermission.getPropertyAsValue(), "In-App Billing", "Specify In-App Billing permission in the manifest"), | props.add (new BooleanPropertyComponent (androidInAppBillingPermission.getPropertyAsValue(), "In-App Billing", "Specify In-App Billing permission in the manifest"), | ||||
"If enabled, this will set the com.android.vending.BILLING flag in the manifest."); | "If enabled, this will set the com.android.vending.BILLING flag in the manifest."); | ||||
props.add (new BooleanPropertyComponent (androidVibratePermission.getPropertyAsValue(), "Vibrate", "Specify permissions to vibrate"), | |||||
"If enabled, this will set the android.permission.VIBRATE flag in the manifest."); | |||||
props.add (new TextPropertyComponent (androidOtherPermissions.getPropertyAsValue(), "Custom permissions", 2048, false), | props.add (new TextPropertyComponent (androidOtherPermissions.getPropertyAsValue(), "Custom permissions", 2048, false), | ||||
"A space-separated list of other permission flags that should be added to the manifest."); | "A space-separated list of other permission flags that should be added to the manifest."); | ||||
props.add (new BooleanPropertyComponent (androidEnableRemoteNotifications.getPropertyAsValue(), "Remote Notifications", "Enabled"), | |||||
"Enable to be able to send remote notifications to devices running your app (min API level 14). Provide Remote Notifications Config File, " | |||||
"configure your app in Firebase Console and ensure you have the latest Google Repository in Android Studio's SDK Manager."); | |||||
props.add (new TextPropertyComponent (androidRemoteNotificationsConfigFile.getPropertyAsValue(), "Remote Notifications Config File", 2048, false), | |||||
"Path to google-services.json file. This will be the file provided by Firebase when creating a new app in Firebase console."); | |||||
props.add (new TextPropertyComponent (androidManifestCustomXmlElements.getPropertyAsValue(), "Custom manifest XML content", 8192, true), | props.add (new TextPropertyComponent (androidManifestCustomXmlElements.getPropertyAsValue(), "Custom manifest XML content", 8192, true), | ||||
"You can specify custom AndroidManifest.xml content overriding the default one generated by Projucer. " | "You can specify custom AndroidManifest.xml content overriding the default one generated by Projucer. " | ||||
"Projucer will automatically create any missing and required XML elements and attributes " | "Projucer will automatically create any missing and required XML elements and attributes " | ||||
@@ -902,10 +956,11 @@ private: | |||||
auto inAppBillingPath = String ("com.android.vending.billing").replaceCharacter ('.', File::getSeparatorChar()); | auto inAppBillingPath = String ("com.android.vending.billing").replaceCharacter ('.', File::getSeparatorChar()); | ||||
auto javaSourceFolder = coreModule->getFolder().getChildFile ("native").getChildFile ("java"); | auto javaSourceFolder = coreModule->getFolder().getChildFile ("native").getChildFile ("java"); | ||||
auto javaInAppBillingTarget = targetFolder.getChildFile ("app/src/main/java").getChildFile (inAppBillingPath); | auto javaInAppBillingTarget = targetFolder.getChildFile ("app/src/main/java").getChildFile (inAppBillingPath); | ||||
auto javaActivityTarget = targetFolder.getChildFile ("app/src/main/java") | |||||
.getChildFile (package.replaceCharacter ('.', File::getSeparatorChar())); | |||||
auto javaTarget = targetFolder.getChildFile ("app/src/main/java") | |||||
.getChildFile (package.replaceCharacter ('.', File::getSeparatorChar())); | |||||
copyActivityJavaFiles (javaSourceFolder, javaActivityTarget, package); | |||||
copyActivityJavaFiles (javaSourceFolder, javaTarget, package); | |||||
copyServicesJavaFiles (javaSourceFolder, javaTarget, package); | |||||
copyAdditionalJavaFiles (javaSourceFolder, javaInAppBillingTarget); | copyAdditionalJavaFiles (javaSourceFolder, javaInAppBillingTarget); | ||||
} | } | ||||
} | } | ||||
@@ -994,21 +1049,69 @@ private: | |||||
inAppBillingJavaSrcFile.copyFileTo (inAppBillingJavaDestFile); | inAppBillingJavaSrcFile.copyFileTo (inAppBillingJavaDestFile); | ||||
} | } | ||||
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 (const auto& file : files) | |||||
{ | |||||
auto javaSourceLines = StringArray::fromLines (file.loadFileAsString()); | |||||
{ | |||||
MemoryOutputStream newFile; | |||||
for (const auto& line : javaSourceLines) | |||||
newFile << line.replace ("package com.juce;", "package " + package + ";") << newLine; | |||||
javaSourceLines = StringArray::fromLines (newFile.toString()); | |||||
} | |||||
auto targetFile = targetFolder.getChildFile (file.getFileName()); | |||||
overwriteFileIfDifferentOrThrow (targetFile, javaSourceLines.joinIntoString (newLine)); | |||||
} | |||||
} | |||||
} | |||||
void copyExtraResourceFiles() const | void copyExtraResourceFiles() const | ||||
{ | { | ||||
for (ConstConfigIterator config (*this); config.next();) | for (ConstConfigIterator config (*this); config.next();) | ||||
{ | { | ||||
const auto& cfg = dynamic_cast<const AndroidBuildConfiguration&> (*config); | const auto& cfg = dynamic_cast<const AndroidBuildConfiguration&> (*config); | ||||
const juce::String path = cfg.isDebug() ? "app/src/debug/res/values" : "app/src/release/res/values"; | |||||
const String xmlValuesPath = cfg.isDebug() ? "app/src/debug/res/values" : "app/src/release/res/values"; | |||||
const String rawPath = cfg.isDebug() ? "app/src/debug/res/raw" : "app/src/release/res/raw"; | |||||
copyExtraResourceFiles (cfg.getAdditionalXmlResources(), path); | |||||
copyExtraResourceFiles (cfg.getAdditionalXmlResources(), xmlValuesPath); | |||||
copyExtraResourceFiles (cfg.getAdditionalRawResources(), rawPath); | |||||
} | |||||
if (androidEnableRemoteNotifications.get()) | |||||
{ | |||||
File file (getProject().getFile().getChildFile (androidRemoteNotificationsConfigFile.get())); | |||||
// Settings file must be present for remote notifications to work and it must be called google-services.json. | |||||
jassert (file.existsAsFile() && file.getFileName() == "google-services.json"); | |||||
copyExtraResourceFiles (androidRemoteNotificationsConfigFile.get(), "app"); | |||||
} | } | ||||
} | } | ||||
void copyExtraResourceFiles (const juce::String& xmlResources, const juce::String& dstRelativePath) const | |||||
void copyExtraResourceFiles (const String& resources, const String& dstRelativePath) const | |||||
{ | { | ||||
juce::StringArray resourcePaths; | |||||
resourcePaths.addTokens (xmlResources, true); | |||||
StringArray resourcePaths; | |||||
resourcePaths.addTokens (resources, true); | |||||
const File parentFolder (getTargetFolder().getChildFile (dstRelativePath)); | const File parentFolder (getTargetFolder().getChildFile (dstRelativePath)); | ||||
@@ -1016,7 +1119,7 @@ private: | |||||
for (const auto& path : resourcePaths) | for (const auto& path : resourcePaths) | ||||
{ | { | ||||
juce::File file (getProject().getFile().getChildFile(path)); | |||||
File file (getProject().getFile().getChildFile (path)); | |||||
jassert (file.existsAsFile()); | jassert (file.existsAsFile()); | ||||
@@ -1076,21 +1179,19 @@ private: | |||||
{ | { | ||||
for (ConstConfigIterator config (*this); config.next();) | for (ConstConfigIterator config (*this); config.next();) | ||||
{ | { | ||||
XmlElement strings ("resources"); | |||||
XmlElement* resourceName = strings.createNewChildElement ("string"); | |||||
resourceName->setAttribute ("name", "app_name"); | |||||
resourceName->addTextElement (projectName); | |||||
const auto& cfg = dynamic_cast<const AndroidBuildConfiguration&> (*config); | const auto& cfg = dynamic_cast<const AndroidBuildConfiguration&> (*config); | ||||
for (XmlElement* e = XmlDocument::parse (cfg.getCustomStringsXml()); e != nullptr; e = e->getNextElement()) | |||||
strings.addChildElement (e); | |||||
String customStringsXmlContent = "<resources>\n"; | |||||
customStringsXmlContent << "<string name=\"app_name\">" << projectName << "</string>\n"; | |||||
customStringsXmlContent << cfg.getCustomStringsXml(); | |||||
customStringsXmlContent << "\n</resources>"; | |||||
ScopedPointer<XmlElement> strings = XmlDocument::parse (customStringsXmlContent); | |||||
const juce::String dir = cfg.isDebug() ? "debug" : "release"; | const juce::String dir = cfg.isDebug() ? "debug" : "release"; | ||||
const juce::String subPath = "app/src/" + dir + "/res/values/string.xml"; | const juce::String subPath = "app/src/" + dir + "/res/values/string.xml"; | ||||
writeXmlOrThrow (strings, folder.getChildFile (subPath), "utf-8", 100, true); | |||||
writeXmlOrThrow (*strings, folder.getChildFile (subPath), "utf-8", 100, true); | |||||
} | } | ||||
} | } | ||||
@@ -1251,10 +1352,19 @@ private: | |||||
defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get()); | defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get()); | ||||
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | ||||
defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\"" + getJNIActivityClassName() + "\""); | defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\"" + getJNIActivityClassName() + "\""); | ||||
defines.set ("JUCE_PUSH_NOTIFICATIONS", "1"); | |||||
if (androidInAppBillingPermission.get()) | if (androidInAppBillingPermission.get()) | ||||
defines.set ("JUCE_IN_APP_PURCHASES", "1"); | 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()) | if (supportsGLv3()) | ||||
defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1"); | defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1"); | ||||
@@ -1475,6 +1585,7 @@ private: | |||||
} | } | ||||
setAttributeIfNotPresent (*act, "android:screenOrientation", androidScreenOrientation.get()); | setAttributeIfNotPresent (*act, "android:screenOrientation", androidScreenOrientation.get()); | ||||
setAttributeIfNotPresent (*act, "android:launchMode", "singleTask"); | |||||
auto* intent = getOrCreateChildWithName (*act, "intent-filter"); | auto* intent = getOrCreateChildWithName (*act, "intent-filter"); | ||||
@@ -1483,6 +1594,23 @@ private: | |||||
auto* category = getOrCreateChildWithName (*intent, "category"); | auto* category = getOrCreateChildWithName (*intent, "category"); | ||||
setAttributeIfNotPresent (*category, "android:name", "android.intent.category.LAUNCHER"); | setAttributeIfNotPresent (*category, "android:name", "android.intent.category.LAUNCHER"); | ||||
if (androidEnableRemoteNotifications.get()) | |||||
{ | |||||
auto* service = app->createNewChildElement ("service"); | |||||
service->setAttribute ("android:name", ".JuceFirebaseMessagingService"); | |||||
auto* intentFilter = service->createNewChildElement ("intent-filter"); | |||||
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.MESSAGING_EVENT"); | |||||
service = app->createNewChildElement ("service"); | |||||
service->setAttribute ("android:name", ".JuceFirebaseInstanceIdService"); | |||||
intentFilter = service->createNewChildElement ("intent-filter"); | |||||
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "com.google.firebase.INSTANCE_ID_EVENT"); | |||||
auto* metaData = app->createNewChildElement ("meta-data"); | |||||
metaData->setAttribute ("android:name", "firebase_analytics_collection_deactivated"); | |||||
metaData->setAttribute ("android:value", "true"); | |||||
} | |||||
} | } | ||||
return manifest; | return manifest; | ||||
@@ -1531,6 +1659,9 @@ private: | |||||
if (androidInAppBillingPermission.get()) | if (androidInAppBillingPermission.get()) | ||||
s.add ("com.android.vending.BILLING"); | s.add ("com.android.vending.BILLING"); | ||||
if (androidVibratePermission.get()) | |||||
s.add ("android.permission.VIBRATE"); | |||||
return getCleanedStringArray (s); | return getCleanedStringArray (s); | ||||
} | } | ||||
@@ -1134,6 +1134,9 @@ public: | |||||
if (owner.isInAppPurchasesEnabled()) | if (owner.isInAppPurchasesEnabled()) | ||||
defines.set ("JUCE_IN_APP_PURCHASES", "1"); | defines.set ("JUCE_IN_APP_PURCHASES", "1"); | ||||
if (owner.iOS && owner.isPushNotificationsEnabled()) | |||||
defines.set ("JUCE_PUSH_NOTIFICATIONS", "1"); | |||||
defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type)); | defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type)); | ||||
StringArray defsList; | StringArray defsList; | ||||
@@ -2243,6 +2246,9 @@ private: | |||||
if (isInAppPurchasesEnabled()) | if (isInAppPurchasesEnabled()) | ||||
xcodeFrameworks.addIfNotAlreadyThere ("StoreKit"); | xcodeFrameworks.addIfNotAlreadyThere ("StoreKit"); | ||||
if (iOS && isPushNotificationsEnabled()) | |||||
xcodeFrameworks.addIfNotAlreadyThere ("UserNotifications"); | |||||
xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'"); | xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'"); | ||||
xcodeFrameworks.trim(); | xcodeFrameworks.trim(); | ||||
@@ -171,6 +171,7 @@ namespace Ids | |||||
DECLARE_ID (androidRepositories); | DECLARE_ID (androidRepositories); | ||||
DECLARE_ID (androidDependencies); | DECLARE_ID (androidDependencies); | ||||
DECLARE_ID (androidAdditionalXmlValueResources); | DECLARE_ID (androidAdditionalXmlValueResources); | ||||
DECLARE_ID (androidAdditionalRawValueResources); | |||||
DECLARE_ID (androidActivityClass); | DECLARE_ID (androidActivityClass); | ||||
DECLARE_ID (androidActivitySubClassName); | DECLARE_ID (androidActivitySubClassName); | ||||
DECLARE_ID (androidVersionCode); | DECLARE_ID (androidVersionCode); | ||||
@@ -184,6 +185,9 @@ namespace Ids | |||||
DECLARE_ID (androidExternalReadNeeded); | DECLARE_ID (androidExternalReadNeeded); | ||||
DECLARE_ID (androidExternalWriteNeeded); | DECLARE_ID (androidExternalWriteNeeded); | ||||
DECLARE_ID (androidInAppBilling); | DECLARE_ID (androidInAppBilling); | ||||
DECLARE_ID (androidVibratePermissionNeeded); | |||||
DECLARE_ID (androidEnableRemoteNotifications); | |||||
DECLARE_ID (androidRemoteNotificationsConfigFile); | |||||
DECLARE_ID (androidMinimumSDK); | DECLARE_ID (androidMinimumSDK); | ||||
DECLARE_ID (androidOtherPermissions); | DECLARE_ID (androidOtherPermissions); | ||||
DECLARE_ID (androidKeyStore); | DECLARE_ID (androidKeyStore); | ||||
@@ -171,6 +171,10 @@ | |||||
#include "native/juce_BasicNativeHeaders.h" | #include "native/juce_BasicNativeHeaders.h" | ||||
#endif | #endif | ||||
#if JUCE_WINDOWS | |||||
#undef small | |||||
#endif | |||||
#include "system/juce_StandardHeader.h" | #include "system/juce_StandardHeader.h" | ||||
namespace juce | namespace juce | ||||
@@ -229,6 +229,7 @@ public class JuceAppActivity extends Activity | |||||
getApplicationInfo().dataDir); | getApplicationInfo().dataDir); | ||||
} | } | ||||
//============================================================================== | |||||
private void hideActionBar() | private void hideActionBar() | ||||
{ | { | ||||
// get "getActionBar" method | // get "getActionBar" method | ||||
@@ -300,6 +301,7 @@ public class JuceAppActivity extends Activity | |||||
private native void resumeApp(); | private native void resumeApp(); | ||||
private native void setScreenSize (int screenWidth, int screenHeight, int dpi); | private native void setScreenSize (int screenWidth, int screenHeight, int dpi); | ||||
private native void appActivityResult (int requestCode, int resultCode, Intent data); | private native void appActivityResult (int requestCode, int resultCode, Intent data); | ||||
private native void appNewIntent (Intent intent); | |||||
//============================================================================== | //============================================================================== | ||||
private ViewHolder viewHolder; | private ViewHolder viewHolder; | ||||
@@ -1367,6 +1369,15 @@ public class JuceAppActivity extends Activity | |||||
appActivityResult (requestCode, resultCode, data); | appActivityResult (requestCode, resultCode, data); | ||||
} | } | ||||
@Override | |||||
protected void onNewIntent (Intent intent) | |||||
{ | |||||
super.onNewIntent(intent); | |||||
setIntent(intent); | |||||
appNewIntent (intent); | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
public final Typeface getTypeFaceFromAsset (String assetName) | public final Typeface getTypeFaceFromAsset (String assetName) | ||||
{ | { | ||||
@@ -0,0 +1,16 @@ | |||||
package com.juce; | |||||
import com.google.firebase.iid.*; | |||||
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService | |||||
{ | |||||
private native void firebaseInstanceIdTokenRefreshed (String token); | |||||
@Override | |||||
public void onTokenRefresh() | |||||
{ | |||||
String token = FirebaseInstanceId.getInstance().getToken(); | |||||
firebaseInstanceIdTokenRefreshed (token); | |||||
} | |||||
} |
@@ -0,0 +1,35 @@ | |||||
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()); | |||||
} | |||||
} |
@@ -138,7 +138,7 @@ public: | |||||
LocalRef& operator= (const LocalRef& other) | LocalRef& operator= (const LocalRef& other) | ||||
{ | { | ||||
jobject newObj = retain (other.obj); | |||||
JavaType newObj = retain (other.obj); | |||||
clear(); | clear(); | ||||
obj = newObj; | obj = newObj; | ||||
return *this; | return *this; | ||||
@@ -168,6 +168,9 @@ namespace | |||||
{ | { | ||||
inline String juceString (JNIEnv* env, jstring s) | inline String juceString (JNIEnv* env, jstring s) | ||||
{ | { | ||||
if (s == 0) | |||||
return {}; | |||||
const char* const utf8 = env->GetStringUTFChars (s, nullptr); | const char* const utf8 = env->GetStringUTFChars (s, nullptr); | ||||
CharPointer_UTF8 utf8CP (utf8); | CharPointer_UTF8 utf8CP (utf8); | ||||
const String result (utf8CP); | const String result (utf8CP); | ||||
@@ -313,11 +316,12 @@ extern AndroidSystem android; | |||||
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ | METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ | ||||
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ | METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ | ||||
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ | METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ | ||||
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ | |||||
METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \ | |||||
STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ | STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ | ||||
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ | METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ | ||||
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ | METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ | ||||
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ | METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ | ||||
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ | |||||
METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \ | METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \ | ||||
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ | METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ | ||||
@@ -399,15 +403,20 @@ DECLARE_JNI_CLASS (JavaObject, "java/lang/Object"); | |||||
#undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | ||||
METHOD (constructor, "<init>", "()V") \ | |||||
METHOD (constructWithString, "<init>", "(Ljava/lang/String;)V") \ | |||||
METHOD (setPackage, "setPackage", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
METHOD (getIntExtra, "getIntExtra", "(Ljava/lang/String;I)I") \ | |||||
METHOD (getStringExtra, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;") \ | |||||
METHOD (addCategory, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
METHOD (setType, "setType", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
METHOD (getData, "getData", "()Landroid/net/Uri;") \ | |||||
METHOD (setAction, "setAction", "(Ljava/lang/String;)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 (getAction, "getAction", "()Ljava/lang/String;") \ | |||||
METHOD (getCategories, "getCategories", "()Ljava/util/Set;") \ | |||||
METHOD (getData, "getData", "()Landroid/net/Uri;") \ | |||||
METHOD (getExtras, "getExtras", "()Landroid/os/Bundle;") \ | |||||
METHOD (getIntExtra, "getIntExtra", "(Ljava/lang/String;I)I") \ | |||||
METHOD (getStringExtra, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;") \ | |||||
METHOD (putExtras, "putExtras", "(Landroid/os/Bundle;)Landroid/content/Intent;") \ | |||||
METHOD (setAction, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
METHOD (setPackage, "setPackage", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
METHOD (setType, "setType", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||||
DECLARE_JNI_CLASS (Intent, "android/content/Intent"); | DECLARE_JNI_CLASS (Intent, "android/content/Intent"); | ||||
#undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
@@ -53,6 +53,11 @@ | |||||
#import <Carbon/Carbon.h> // still needed for SetSystemUIMode() | #import <Carbon/Carbon.h> // still needed for SetSystemUIMode() | ||||
#endif | #endif | ||||
#elif JUCE_IOS | |||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
#import <UserNotifications/UserNotifications.h> | |||||
#endif | |||||
//============================================================================== | //============================================================================== | ||||
#elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
#include <windowsx.h> | #include <windowsx.h> | ||||
@@ -30,6 +30,16 @@ namespace juce | |||||
{ | { | ||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_PUSH_NOTIFICATIONS && JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
// Returns true if the intent was handled. | |||||
extern bool juce_handleNotificationIntent (void*); | |||||
extern void juce_firebaseDeviceNotificationsTokenRefreshed (void*); | |||||
extern void juce_firebaseRemoteNotificationReceived (void*); | |||||
extern void juce_firebaseRemoteMessagesDeleted(); | |||||
extern void juce_firebaseRemoteMessageSent(void*); | |||||
extern void juce_firebaseRemoteMessageSendError (void*, void*); | |||||
#endif | |||||
#if JUCE_IN_APP_PURCHASES && JUCE_MODULE_AVAILABLE_juce_product_unlocking | #if JUCE_IN_APP_PURCHASES && JUCE_MODULE_AVAILABLE_juce_product_unlocking | ||||
extern void juce_inAppPurchaseCompleted (void*); | extern void juce_inAppPurchaseCompleted (void*); | ||||
#endif | #endif | ||||
@@ -98,6 +108,75 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, appActivityResult, void, (JN | |||||
#endif | #endif | ||||
} | } | ||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, appNewIntent, void, (JNIEnv* env, jobject, jobject intentData)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_PUSH_NOTIFICATIONS && JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
if (juce_handleNotificationIntent ((void *)intentData)) | |||||
return; | |||||
// Add other functions processing intents here as needed. | |||||
#else | |||||
ignoreUnused (intentData); | |||||
#endif | |||||
} | |||||
#if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) | |||||
JUCE_JNI_CALLBACK (JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME, firebaseInstanceIdTokenRefreshed, void, (JNIEnv* env, jobject /*activity*/, jstring token)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
juce_firebaseDeviceNotificationsTokenRefreshed (token); | |||||
#else | |||||
ignoreUnused (token); | |||||
#endif | |||||
} | |||||
JUCE_JNI_CALLBACK (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME, firebaseRemoteMessageReceived, void, (JNIEnv* env, jobject /*activity*/, jobject remoteMessage)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
juce_firebaseRemoteNotificationReceived (remoteMessage); | |||||
#else | |||||
ignoreUnused (remoteMessage); | |||||
#endif | |||||
} | |||||
JUCE_JNI_CALLBACK (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME, firebaseRemoteMessagesDeleted, void, (JNIEnv* env, jobject /*activity*/)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
juce_firebaseRemoteMessagesDeleted(); | |||||
#endif | |||||
} | |||||
JUCE_JNI_CALLBACK (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME, firebaseRemoteMessageSent, void, (JNIEnv* env, jobject /*activity*/, jstring messageId)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
juce_firebaseRemoteMessageSent (messageId); | |||||
#else | |||||
ignoreUnused (messageId); | |||||
#endif | |||||
} | |||||
JUCE_JNI_CALLBACK (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME, firebaseRemoteMessageSendError, void, (JNIEnv* env, jobject /*activity*/, jstring messageId, jstring error)) | |||||
{ | |||||
setEnv (env); | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
juce_firebaseRemoteMessageSendError (messageId, error); | |||||
#else | |||||
ignoreUnused (messageId, error); | |||||
#endif | |||||
} | |||||
#endif | |||||
//============================================================================== | //============================================================================== | ||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | ||||
METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \ | METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \ | ||||
@@ -38,7 +38,11 @@ namespace juce | |||||
Array<AppInactivityCallback*> appBecomingInactiveCallbacks; | Array<AppInactivityCallback*> appBecomingInactiveCallbacks; | ||||
} | } | ||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
@interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate, UNUserNotificationCenterDelegate> | |||||
#else | |||||
@interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate> | @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate> | ||||
#endif | |||||
{ | { | ||||
UIBackgroundTaskIdentifier appSuspendTask; | UIBackgroundTaskIdentifier appSuspendTask; | ||||
} | } | ||||
@@ -51,17 +55,45 @@ namespace juce | |||||
- (void) applicationWillEnterForeground: (UIApplication*) application; | - (void) applicationWillEnterForeground: (UIApplication*) application; | ||||
- (void) applicationDidBecomeActive: (UIApplication*) application; | - (void) applicationDidBecomeActive: (UIApplication*) application; | ||||
- (void) applicationWillResignActive: (UIApplication*) application; | - (void) applicationWillResignActive: (UIApplication*) application; | ||||
- (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier | |||||
completionHandler: (void (^)(void))completionHandler; | |||||
- (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*) identifier | |||||
completionHandler: (void (^)(void)) completionHandler; | |||||
- (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings; | |||||
- (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken; | |||||
- (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error; | |||||
- (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo; | |||||
- (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo | |||||
fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler; | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo | |||||
completionHandler: (void(^)()) completionHandler; | |||||
- (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification; | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler; | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo | |||||
completionHandler: (void(^)()) completionHandler; | |||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
- (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification | |||||
withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler; | |||||
- (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response | |||||
withCompletionHandler: (void(^)())completionHandler; | |||||
#endif | |||||
@end | @end | ||||
@implementation JuceAppStartupDelegate | @implementation JuceAppStartupDelegate | ||||
NSObject* _pushNotificationsDelegate; | |||||
- (id)init | - (id)init | ||||
{ | { | ||||
self = [super init]; | self = [super init]; | ||||
appSuspendTask = UIBackgroundTaskInvalid; | appSuspendTask = UIBackgroundTaskInvalid; | ||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self; | |||||
#endif | |||||
return self; | return self; | ||||
} | } | ||||
@@ -118,7 +150,8 @@ namespace juce | |||||
- (void) applicationDidBecomeActive: (UIApplication*) application | - (void) applicationDidBecomeActive: (UIApplication*) application | ||||
{ | { | ||||
ignoreUnused (application); | |||||
application.applicationIconBadgeNumber = 0; | |||||
isIOSAppActive = true; | isIOSAppActive = true; | ||||
} | } | ||||
@@ -139,6 +172,230 @@ namespace juce | |||||
completionHandler(); | completionHandler(); | ||||
} | } | ||||
- (void) setPushNotificationsDelegateToUse: (NSObject*) delegate | |||||
{ | |||||
_pushNotificationsDelegate = delegate; | |||||
} | |||||
- (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didRegisterUserNotificationSettings:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: ¬ificationSettings atIndex:3]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didRegisterForRemoteNotificationsWithDeviceToken:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &deviceToken atIndex:3]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didFailToRegisterForRemoteNotificationsWithError:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &error atIndex:3]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didReceiveRemoteNotification:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &userInfo atIndex:3]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo | |||||
fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didReceiveRemoteNotification:fetchCompletionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &userInfo atIndex:3]; | |||||
[invocation setArgument: &completionHandler atIndex:4]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo | |||||
completionHandler: (void(^)()) completionHandler | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &identifier atIndex:3]; | |||||
[invocation setArgument: &userInfo atIndex:4]; | |||||
[invocation setArgument: &responseInfo atIndex:5]; | |||||
[invocation setArgument: &completionHandler atIndex:6]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:didReceiveLocalNotification:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: ¬ification atIndex:3]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:handleActionWithIdentifier:forLocalNotification:completionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &identifier atIndex:3]; | |||||
[invocation setArgument: ¬ification atIndex:4]; | |||||
[invocation setArgument: &completionHandler atIndex:5]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier | |||||
forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo | |||||
completionHandler: (void(^)()) completionHandler | |||||
{ | |||||
ignoreUnused (application); | |||||
SEL selector = NSSelectorFromString (@"application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: &application atIndex:2]; | |||||
[invocation setArgument: &identifier atIndex:3]; | |||||
[invocation setArgument: ¬ification atIndex:4]; | |||||
[invocation setArgument: &responseInfo atIndex:5]; | |||||
[invocation setArgument: &completionHandler atIndex:6]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
- (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification | |||||
withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler | |||||
{ | |||||
ignoreUnused (center); | |||||
SEL selector = NSSelectorFromString (@"userNotificationCenter:willPresentNotification:withCompletionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: ¢er atIndex:2]; | |||||
[invocation setArgument: ¬ification atIndex:3]; | |||||
[invocation setArgument: &completionHandler atIndex:4]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
- (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response | |||||
withCompletionHandler: (void(^)()) completionHandler | |||||
{ | |||||
ignoreUnused (center); | |||||
SEL selector = NSSelectorFromString (@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"); | |||||
if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) | |||||
{ | |||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; | |||||
[invocation setSelector: selector]; | |||||
[invocation setTarget: _pushNotificationsDelegate]; | |||||
[invocation setArgument: ¢er atIndex:2]; | |||||
[invocation setArgument: &response atIndex:3]; | |||||
[invocation setArgument: &completionHandler atIndex:4]; | |||||
[invocation invoke]; | |||||
} | |||||
} | |||||
#endif | |||||
@end | @end | ||||
namespace juce | namespace juce | ||||
@@ -35,10 +35,15 @@ | |||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | #define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | ||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | #define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | ||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | ||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | #define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | ||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | ||||
#ifndef JUCE_PUSH_NOTIFICATIONS | |||||
#define JUCE_PUSH_NOTIFICATIONS 0 | |||||
#endif | |||||
#include "juce_gui_extra.h" | #include "juce_gui_extra.h" | ||||
//============================================================================== | //============================================================================== | ||||
@@ -50,7 +55,21 @@ | |||||
#import <IOKit/hid/IOHIDKeys.h> | #import <IOKit/hid/IOHIDKeys.h> | ||||
#import <IOKit/pwr_mgt/IOPMLib.h> | #import <IOKit/pwr_mgt/IOPMLib.h> | ||||
//============================================================================== | |||||
#elif JUCE_IOS | #elif JUCE_IOS | ||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
#if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
#import <UserNotifications/UserNotifications.h> | |||||
#endif | |||||
#include "native/juce_ios_PushNotifications.cpp" | |||||
#endif | |||||
//============================================================================== | |||||
#elif JUCE_ANDROID | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
#include "native/juce_android_PushNotifications.cpp" | |||||
#endif | |||||
//============================================================================== | //============================================================================== | ||||
#elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
@@ -93,6 +112,7 @@ | |||||
#include "misc/juce_ColourSelector.cpp" | #include "misc/juce_ColourSelector.cpp" | ||||
#include "misc/juce_KeyMappingEditorComponent.cpp" | #include "misc/juce_KeyMappingEditorComponent.cpp" | ||||
#include "misc/juce_PreferencesPanel.cpp" | #include "misc/juce_PreferencesPanel.cpp" | ||||
#include "misc/juce_PushNotifications.cpp" | |||||
#include "misc/juce_RecentlyOpenedFilesList.cpp" | #include "misc/juce_RecentlyOpenedFilesList.cpp" | ||||
#include "misc/juce_SplashScreen.cpp" | #include "misc/juce_SplashScreen.cpp" | ||||
#include "misc/juce_SystemTrayIconComponent.cpp" | #include "misc/juce_SystemTrayIconComponent.cpp" | ||||
@@ -91,6 +91,7 @@ | |||||
#include "misc/juce_ColourSelector.h" | #include "misc/juce_ColourSelector.h" | ||||
#include "misc/juce_KeyMappingEditorComponent.h" | #include "misc/juce_KeyMappingEditorComponent.h" | ||||
#include "misc/juce_PreferencesPanel.h" | #include "misc/juce_PreferencesPanel.h" | ||||
#include "misc/juce_PushNotifications.h" | |||||
#include "misc/juce_RecentlyOpenedFilesList.h" | #include "misc/juce_RecentlyOpenedFilesList.h" | ||||
#include "misc/juce_SplashScreen.h" | #include "misc/juce_SplashScreen.h" | ||||
#include "misc/juce_SystemTrayIconComponent.h" | #include "misc/juce_SystemTrayIconComponent.h" | ||||
@@ -0,0 +1,190 @@ | |||||
/* | |||||
============================================================================== | |||||
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. | |||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
27th April 2017). | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
Or: You may also use this code under the terms of the GPL v3 (see | |||||
www.gnu.org/licenses). | |||||
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 | |||||
{ | |||||
//============================================================================== | |||||
#if ! JUCE_ANDROID && ! JUCE_IOS | |||||
bool PushNotifications::Notification::isValid() const noexcept { return true; } | |||||
#endif | |||||
//============================================================================== | |||||
juce_ImplementSingleton (PushNotifications) | |||||
PushNotifications::PushNotifications() | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
: pimpl (new Pimpl (*this)) | |||||
#endif | |||||
{ | |||||
} | |||||
PushNotifications::~PushNotifications() { clearSingletonInstance(); } | |||||
void PushNotifications::addListener (Listener* l) { listeners.add (l); } | |||||
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); } | |||||
void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS && JUCE_IOS | |||||
pimpl->requestPermissionsWithSettings (settings); | |||||
#else | |||||
ignoreUnused (settings); | |||||
listeners.call (&PushNotifications::Listener::notificationSettingsReceived, {}); | |||||
#endif | |||||
} | |||||
void PushNotifications::requestSettingsUsed() | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS && JUCE_IOS | |||||
pimpl->requestSettingsUsed(); | |||||
#else | |||||
listeners.call (&PushNotifications::Listener::notificationSettingsReceived, {}); | |||||
#endif | |||||
} | |||||
bool PushNotifications::areNotificationsEnabled() const | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
return pimpl->areNotificationsEnabled(); | |||||
#else | |||||
return false; | |||||
#endif | |||||
} | |||||
void PushNotifications::getDeliveredNotifications() const | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->getDeliveredNotifications(); | |||||
#endif | |||||
} | |||||
void PushNotifications::removeAllDeliveredNotifications() | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->removeAllDeliveredNotifications(); | |||||
#endif | |||||
} | |||||
String PushNotifications::getDeviceToken() const | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
return pimpl->getDeviceToken(); | |||||
#else | |||||
return {}; | |||||
#endif | |||||
} | |||||
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->setupChannels (groups, channels); | |||||
#else | |||||
ignoreUnused (groups, channels); | |||||
#endif | |||||
} | |||||
void PushNotifications::getPendingLocalNotifications() const | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->getPendingLocalNotifications(); | |||||
#endif | |||||
} | |||||
void PushNotifications::removeAllPendingLocalNotifications() | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->removeAllPendingLocalNotifications(); | |||||
#endif | |||||
} | |||||
void PushNotifications::subscribeToTopic (const String& topic) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->subscribeToTopic (topic); | |||||
#else | |||||
ignoreUnused (topic); | |||||
#endif | |||||
} | |||||
void PushNotifications::unsubscribeFromTopic (const String& topic) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->unsubscribeFromTopic (topic); | |||||
#else | |||||
ignoreUnused (topic); | |||||
#endif | |||||
} | |||||
void PushNotifications::sendLocalNotification (const Notification& n) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->sendLocalNotification (n); | |||||
#else | |||||
ignoreUnused (n); | |||||
#endif | |||||
} | |||||
void PushNotifications::removeDeliveredNotification (const String& identifier) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->removeDeliveredNotification (identifier); | |||||
#else | |||||
ignoreUnused (identifier); | |||||
#endif | |||||
} | |||||
void PushNotifications::removePendingLocalNotification (const String& identifier) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->removePendingLocalNotification (identifier); | |||||
#else | |||||
ignoreUnused (identifier); | |||||
#endif | |||||
} | |||||
void PushNotifications::sendUpstreamMessage (const String& serverSenderId, | |||||
const String& collapseKey, | |||||
const String& messageId, | |||||
const String& messageType, | |||||
int timeToLive, | |||||
const StringPairArray& additionalData) | |||||
{ | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
pimpl->sendUpstreamMessage (serverSenderId, | |||||
collapseKey, | |||||
messageId, | |||||
messageType, | |||||
timeToLive, | |||||
additionalData); | |||||
#else | |||||
ignoreUnused (serverSenderId, collapseKey, messageId, messageType); | |||||
ignoreUnused (timeToLive, additionalData); | |||||
#endif | |||||
} | |||||
} // namespace juce |
@@ -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. | |||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
27th April 2017). | |||||
End User License Agreement: www.juce.com/juce-5-licence | |||||
Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
Or: You may also use this code under the terms of the GPL v3 (see | |||||
www.gnu.org/licenses). | |||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||||
DISCLAIMED. | |||||
============================================================================== | |||||
*/ | |||||
#pragma once | |||||
namespace juce | |||||
{ | |||||
/** Singleton class responsible for push notifications functionality. Both remote and | |||||
local notifications are supported. To get information about notifications, | |||||
register a listener on your application startup. It is best to register the | |||||
listener as soon as possible, because your application can be launched from | |||||
a push notification too. | |||||
To send a local notification create an instance of @class Notification, fill the | |||||
necessary fields and call PushNotifications::sendLocalNotification(). When receiving | |||||
local or remote notifications, inspect the Notification's fields for notification details. | |||||
Bear in mind that some fields will not be available when receiving a remote notification. | |||||
*/ | |||||
class JUCE_API PushNotifications | |||||
{ | |||||
public: | |||||
juce_DeclareSingleton (PushNotifications, false) | |||||
//========================================================================== | |||||
/** Represents a notification that can be sent or received. */ | |||||
struct Notification | |||||
{ | |||||
Notification() = default; | |||||
/** Checks whether a given notification is correctly configured for a given OS. */ | |||||
bool isValid() const noexcept; | |||||
/** Represents an action on a notification that can be presented as a button or a text input. | |||||
On Android, each notification has its action specified explicitly, on iOS you configure an | |||||
allowed set of actions on startup and pack them into categories (see @class Settings). | |||||
*/ | |||||
struct Action | |||||
{ | |||||
/** Controls the appearance of this action. */ | |||||
enum Style | |||||
{ | |||||
button, /**< Show this action as a button. */ | |||||
text /**< Show this action as a text input field (on Android API 20 or higher is required). */ | |||||
}; | |||||
/** @name Common fields */ | |||||
/**@{*/ | |||||
Style style = button; | |||||
String title; /**< Required. the name of the action displayed to the user. */ | |||||
String textInputPlaceholder; /**< Optional: placeholder text for text input notification. | |||||
Note that it will be ignored if button style is used. */ | |||||
var parameters; /**< Optional: additional parameters that can be passed. */ | |||||
/**@}*/ | |||||
/** @name iOS only fields */ | |||||
/**@{*/ | |||||
String identifier; /**< Required: unique identifier. This should be one of the | |||||
identifiers set with requestPermissionsWithSettings(). */ | |||||
bool triggerInBackground = false; /**< Whether the app can process the action in background. */ | |||||
bool destructive = false; /**< Whether to display the action as destructive. */ | |||||
String textInputButtonText; /**< Optional: Text displayed on text input notification | |||||
button (from iOS 10 only). | |||||
Note that it will be ignored if style is set to Style::button. */ | |||||
/**@}*/ | |||||
/** @name Android only fields */ | |||||
/**@{*/ | |||||
String icon; /**< Optional: name of an icon file (without an extension) to be used for | |||||
this action. This must be the name of one of the image | |||||
files included into resources when exporting an Android project | |||||
(see "Extra Android Raw Resources" setting in Projucer). | |||||
Note that not all Android versions support an icon for an action, though | |||||
it is recommended to provide it nevertheless. */ | |||||
StringArray allowedResponses; /**< Optional: a list of possible answers if the answer set is limited. | |||||
When left empty, then the user will be able to input any text. */ | |||||
/**@}*/ | |||||
}; | |||||
//========================================================================== | |||||
/** @name Common fields */ | |||||
/**@{*/ | |||||
String identifier; /**< Required: unique id that can be used to later dismiss the notification | |||||
(on iOS available from version 10). */ | |||||
String title; /**< Required: the title of the notification, usually displayed in the first row. */ | |||||
String body; /**< Required: the content of the notification, usually displayed in the second row. */ | |||||
String subtitle; /**< Optional: additional text, that may be displayed e.g. in the third row or in the header | |||||
area. Note that on Android, depending on OS version, this may fight for | |||||
space with other components of the notification, so use this field | |||||
judiciously. On iOS available from version 10. On Android available from API 16. */ | |||||
String groupId; /**< Optional: allows the OS to visually group, collapse, and expand a set of notifications, | |||||
note that OS may automatically group notifications if no groupId is specified. | |||||
Available on Android API 20 or above and iOS 10 or above. */ | |||||
int badgeNumber = 0; /**< Optional: on platforms that support it, can set a number this notification represents. */ | |||||
URL soundToPlay; /**< Optional: empty when the notification should be silent. When the name is set to | |||||
"default_os_sound", then a default sound will be used. | |||||
For a custom sound on iOS, set the URL to a relative path within your bundle, including | |||||
file extension. For instance, if your bundle contains "sounds" folder with "my_sound.caf" | |||||
file, then the URL should be "sounds/my_sound.caf". | |||||
For a custom sound on Android, set URL to the name of a raw resource file | |||||
(without an extention) that was included when exporting an Android project in | |||||
Projucer (see "Extra Android Raw Resources" setting). */ | |||||
var properties; /**< Optional: collection of additional properties that may be passed as a dictionary. */ | |||||
/**@}*/ | |||||
//========================================================================== | |||||
/** @name iOS only fields */ | |||||
/**@{*/ | |||||
String category; /**< Required: determines set of actions that will appear (as per setup done | |||||
in initializeWithSettings()). */ | |||||
double triggerIntervalSec = 0.; /**< Optional: specifies number of seconds before the notification should trigger. */ | |||||
bool repeat = false; /**< Optional: allows the notification to continuously retrigger after | |||||
triggerIntervalSec seconds. Available from iOS 10. */ | |||||
/**@}*/ | |||||
//========================================================================== | |||||
/** @name Android only fields */ | |||||
/**@{*/ | |||||
String icon; /**< Required: name of an icon file (without an extension) to be used for | |||||
this notification. This must be the name of one of the image | |||||
files included into resources when exporting an Android project | |||||
(see "Extra Android Raw Resources" setting in Projucer). */ | |||||
String channelId; /**< Required for Android API level 26 or above: specifies notification channel id. Refer to | |||||
setupChannels(). Ignored on earlier Android versions. */ | |||||
Image largeIcon; /**< Optional: an additional large icon displayed in the notification content view. */ | |||||
String tickerText; /**< Optional: ticker text used for accessibility services. */ | |||||
Array<Action> actions; /**< Optional: actions associated with the notification. Note that the OS may allow only a limited | |||||
number of actions to be presented, so always present most important actions first. | |||||
Available from Android API 16 or above. */ | |||||
/**< Used to represent a progress of some operation. */ | |||||
struct Progress | |||||
{ | |||||
int max = 0; /**< Max possible value of a progress. A typical usecase is to set max to 100 and increment | |||||
current's value as percentage complete. */ | |||||
int current = 0; /**< Current progress value, should be from 0 to max. */ | |||||
bool indeterminate = false; /**< If true, then the progress represents a continuing activity indicator with ongoing | |||||
animation and no numeric value. */ | |||||
}; | |||||
Progress progress; /**< Optional: set to default (0, 0, false), to disable progress display. */ | |||||
/** Metadata that can be used by the OS to better handle the notification, depending on its priority. */ | |||||
enum Type | |||||
{ | |||||
unspecified, /**< Category not set. */ | |||||
alarm, /**< Alarm or timer. */ | |||||
call, /**< Incoming voice/video call or similar. */ | |||||
email, /**< Async message like email. */ | |||||
error, /**< Error in background operation or authentication status. */ | |||||
event, /**< Calendar event. */ | |||||
message, /**< Incoming message (sms, instant message etc.). */ | |||||
taskProgress, /**< Progress for a long-running background operation. */ | |||||
promo, /**< Promotion or advertisement. */ | |||||
recommendation, /**< Specific, single thing related recommendation. */ | |||||
reminder, /**< User-scheduled reminder. */ | |||||
service, /**< Running background service. */ | |||||
social, /**< Social network or sharing update. */ | |||||
status, /**< Ongoing information about device or contextual status. */ | |||||
system, /**< System or device status update. */ | |||||
transport /**< Media transport control for playback. */ | |||||
}; | |||||
/** Metadata used as a hint to the OS about the priority of the notification. */ | |||||
enum Priority | |||||
{ | |||||
veryLow = -2, | |||||
low = -1, | |||||
medium = 0, | |||||
high = 1, | |||||
veryHigh = 2 | |||||
}; | |||||
String person; /**< Optional: additional metadata used as a hint to OS that a notification is | |||||
related to a specific person. Can be useful for instance messaging apps. | |||||
Available from Android API 21 or above. */ | |||||
Type type = unspecified; /**< Optional. Available from Android API 21 or above. */ | |||||
Priority priority = medium; /**< Optional. Available from Android API 16 or above. */ | |||||
/** Describes how to show the notification when the screen is locked. Available from Android API 21 or above. */ | |||||
enum LockScreenAppearance | |||||
{ | |||||
dontShow = -1, /**< The notification is not allowed on the lock screen */ | |||||
showPartially = 0, /**< Only some information is allowed on the lock screen */ | |||||
showCompletely = 1 /**< The entire notification is allowed on the lock screen */ | |||||
}; | |||||
LockScreenAppearance lockScreenAppearance = showPartially; /**< Optional. */ | |||||
ScopedPointer<Notification> publicVersion; /**< Optional: if you set lockScreenAppearance to showPartially, | |||||
then you can provide "public version" of your notification | |||||
that will be displayed on the lock screen. This way you can | |||||
control what information is visible when the screen is locked. */ | |||||
String groupSortKey; /**< Optional: Used to order notifications within the same group. Available from Android API 20 or above. */ | |||||
bool groupSummary = false; /**< Optional: if true, then this notification will be a group summary of the group set with groupId. | |||||
Available from Android API 20 or above. */ | |||||
Colour accentColour; /**< Optional: sets accent colour. The default colour will be used if accentColour is not set. | |||||
Available from Android API 21 or above. */ | |||||
Colour ledColour; /**< Optional: Sets the led colour. The hardware will do its best to approximate the colour. | |||||
The default colour will be used if ledColour is not set. */ | |||||
/**< Allows to control the time the device's led is on and off. */ | |||||
struct LedBlinkPattern | |||||
{ | |||||
int msToBeOn = 0; /**< The led will be on for the given number of milliseconds, after which it will turn off. */ | |||||
int msToBeOff = 0; /**< The led will be off for the given number of milliseconds, after which it will turn on. */ | |||||
}; | |||||
LedBlinkPattern ledBlinkPattern; /**< Optional. */ | |||||
Array<int> vibrationPattern; /**< Optional: sets the vibration pattern in milliseconds. The first value indicates how long | |||||
to wait until vibration starts. The second value indicates how long to vibrate. The third | |||||
value will say how long to not vibrate and so on. For instance, if the pattern is: | |||||
1000, 2000, 3000, 4000 - then one second after receiving a notification the device will | |||||
vibrate for two seconds, followed by 3 seconds of no vibration and finally, 4 seconds of | |||||
vibration. */ | |||||
bool shouldAutoCancel = true; /**< Optional: If true, the notification will be automatically cancelled when a user clicks it in the panel. */ | |||||
bool localOnly = true; /**< Optional: whether or not the notification should bridge to other devices. | |||||
Available from Android API 20 or above. */ | |||||
bool ongoing = false; /**< Optional: If true, then it cannot be dismissed by the user and it must be dimissed manually. | |||||
Typically used for ongoing background tasks that the user is actively engaged with. To | |||||
dismiss such notification, you need to call removeDeliveredNotification() or | |||||
removeAllDeliveredNotifications(). */ | |||||
bool alertOnlyOnce = false; /**< Optional: Set this flag if you would only like the sound, vibrate and ticker to be played if the notification | |||||
is not already showing. */ | |||||
/**< Controls timestamp visibility and format. */ | |||||
enum TimestampVisibility | |||||
{ | |||||
off, /**< Do not show timestamp. */ | |||||
normal, /**< Show normal timestamp. */ | |||||
chronometer, /**< Show chronometer as a stopwatch. Available from Android API 16 or above. */ | |||||
countDownChronometer /**< Set the chronometer to count down instead of counting up. Available from Android API 24 or above.*/ | |||||
}; | |||||
TimestampVisibility timestampVisibility = normal; /**< Optional. */ | |||||
/**< Controls badge icon type to use if a notification is shown as a badge. Available from Android API 26 or above. */ | |||||
enum BadgeIconType | |||||
{ | |||||
none, | |||||
small, | |||||
large | |||||
}; | |||||
BadgeIconType badgeIconType = large; | |||||
/** Controls sound and vibration behaviour for group notifications. Available from Android API 26 or above. */ | |||||
enum GroupAlertBehaviour | |||||
{ | |||||
alertAll, /**< both child notifications and group notifications should produce sound and vibration. */ | |||||
AlertSummary, /**< all child notifications in the group should have no sound nor vibration, even | |||||
if corresponding notification channel has sounds and vibrations enabled. */ | |||||
AlertChildren /**< summary notifications in the group should have no sound nor vibration, even if | |||||
corresponding notification channel has sounds and vibrations enabled. */ | |||||
}; | |||||
GroupAlertBehaviour groupAlertBehaviour = alertAll; | |||||
int timeoutAfterMs = 0; /**< specifies a duration in milliseconds, after which the notification should be | |||||
cancelled, if it is not already canceled. Available from Android API 26 or above. */ | |||||
/**@}*/ | |||||
}; | |||||
//========================================================================== | |||||
/** Describes settings we want to use for current device. Note that at the | |||||
moment this is only used on iOS. | |||||
To setup push notifications for current device, provide permissions required, | |||||
as well as register categories of notifications you want to support. Each | |||||
category needs to have a unique identifier and it can optionally have multiple | |||||
actions. Each action also needs to have a unique identifier. The example setup | |||||
may look as follows: | |||||
@code | |||||
using Action = PushNotifications::Settings::Action; | |||||
using Category = PushNotifications::Settings::Category; | |||||
Action okAction; | |||||
okAction.identifier = "okAction"; | |||||
okAction.title = "OK!"; | |||||
okAction.style = Action::button; | |||||
okAction.triggerInBackground = true; | |||||
Action cancelAction; | |||||
cancelAction.identifier = "cancelAction"; | |||||
cancelAction.title = "Cancel"; | |||||
cancelAction.style = Action::button; | |||||
cancelAction.triggerInBackground = true; | |||||
cancelAction.destructive = true; | |||||
Action textAction; | |||||
textAction.identifier = "textAction"; | |||||
textAction.title = "Enter text"; | |||||
textAction.style = Action::text; | |||||
textAction.triggerInBackground = true; | |||||
textAction.destructive = false; | |||||
textAction.textInputButtonText = "Ok"; | |||||
textAction.textInputPlaceholder = "Enter text..."; | |||||
Category okCategory; | |||||
okCategory.identifier = "okCategory"; | |||||
okCategory.actions = { okAction }; | |||||
Category okCancelCategory; | |||||
okCancelCategory.identifier = "okCancelCategory"; | |||||
okCancelCategory.actions = { okAction, cancelAction }; | |||||
Category textCategory; | |||||
textCategory.identifier = "textCategory"; | |||||
textCategory.actions = { textAction }; | |||||
textCategory.sendDismissAction = true; | |||||
PushNotifications::Settings settings; | |||||
settings.allowAlert = true; | |||||
settings.allowBadge = true; | |||||
settings.allowSound = true; | |||||
settings.categories = { okCategory, okCancelCategory, textCategory }; | |||||
@endcode | |||||
*/ | |||||
struct Settings | |||||
{ | |||||
using Action = Notification::Action; | |||||
/** Describes a category of a notification. Each category has a unique idenfifier | |||||
and a list of associated actions. | |||||
Note that the OS may allow only a limited number of actions to be presented, so | |||||
always present most important actions first. | |||||
*/ | |||||
struct Category | |||||
{ | |||||
juce::String identifier; /**< unique indentifier */ | |||||
juce::Array<Action> actions; /**< optional list of actions within this category */ | |||||
bool sendDismissAction = false; /**< whether dismiss action will be sent to the app (from iOS 10 only) */ | |||||
}; | |||||
bool allowSound = false; /**< whether the app should play a sound upon notification */ | |||||
bool allowAlert = false; /**< whether the app should present an alert upon notification */ | |||||
bool allowBadge = false; /**< whether the app may badge its icon upon notification */ | |||||
Array<Category> categories; /**< list of categories the app wants to support */ | |||||
}; | |||||
/** Initializes push notifications on current device with the settings provided. | |||||
Call this on your application startup and on iOS the first time the application starts, | |||||
a user will be presented with a permission request dialog to give push notifications permission. | |||||
Once a user responds, Listener::notificationSettingsReceived() will be called so that | |||||
you can check what permissions where actually granted. The listener callback will be called | |||||
on each subsequent startup too (provided you called requestPermissionsWithSettings() on previous | |||||
application run). This way you can check what are current push notifications permissions. | |||||
Note that settings are currently only used on iOS. When calling on other platforms, Settings | |||||
with no categories and all allow* flags set to true will be received in | |||||
Listener::notificationSettingsReceived(). | |||||
You can also call requestSettingsUsed() to explicitly ask for current settings. | |||||
*/ | |||||
void requestPermissionsWithSettings (const Settings& s); | |||||
/** Sends an asynchronous request to retrieve current settings that are currently in use. | |||||
These can be exactly the same as used in requestPermissionsWithSettings(), but depending | |||||
on user's subsequent changes in OS settings, the actual current settings may be | |||||
different (e.g. user might have later decided to disable sounds). | |||||
Note that settings are currently only used on iOS. When calling on other platforms, Settings | |||||
with no categories and all allow* flags set to true will be received in | |||||
Listener::notificationSettingsReceived(). | |||||
*/ | |||||
void requestSettingsUsed(); | |||||
//========================================================================== | |||||
/** Android API level 26 or higher only: Represents notification channel through which | |||||
notifications will be sent. Starting from Android API level 26, you should call setupChannels() | |||||
at the start of your application, before posting any notifications. Then, when sending notifications, | |||||
assign a channel to each created notification. | |||||
*/ | |||||
struct Channel | |||||
{ | |||||
String identifier; /**< Required: Unique channel identifier. */ | |||||
String name; /**< Required: User facing name of the channel. */ | |||||
/** Controls how interruptive the notification posted on this channel are. */ | |||||
enum Importance | |||||
{ | |||||
none, | |||||
min, | |||||
low, | |||||
normal, | |||||
high, | |||||
max | |||||
}; | |||||
Importance importance = normal; /**< Required. */ | |||||
Notification::LockScreenAppearance lockScreenAppearance = Notification::showPartially; /**< Optional. */ | |||||
String description; /**< Optional: user visible description of the channel. */ | |||||
String groupId; /**< Required: group this channel belongs to (see @class ChannelGroup). */ | |||||
Colour ledColour; /**< Optional: sets the led colour for notifications in this channel. */ | |||||
bool bypassDoNotDisturb = false; /**< Optional: true if notifications in this channel can bypass do not disturb setting. */ | |||||
bool canShowBadge = false; /**< Optional: true if notifications in this channel can show badges in a Launcher application. */ | |||||
bool enableLights = false; /**< Optional: true if notifications in this channel should show lights (subject to hardware support). */ | |||||
bool enableVibration = false; /**< Optional: true if notifications in this channel should trigger vibrations. */ | |||||
URL soundToPlay; /**< Optional: sound to play in this channel. See Notification::soundToPlay for more info. */ | |||||
Array<int> vibrationPattern; /**< Optional: vibration pattern for this channel. See Notification::vibrationPattern for more info. */ | |||||
}; | |||||
/** Android API level 26 or higher only: represents a channel group. This allows for | |||||
visual grouping of corresponding channels in notification settings presented to the user. | |||||
At least one channel group has to be specified before notifications can be sent. | |||||
*/ | |||||
struct ChannelGroup | |||||
{ | |||||
String identifier; /**< Required: Unique channel group identifier. */ | |||||
String name; /**< Required: User visible name of the channel group. */ | |||||
}; | |||||
/** Android API level 26 or higher only: configures notification channel groups and channels to be | |||||
used in the app. These have to be setup before notifications can be sent on Android API | |||||
level 26 or higher. | |||||
*/ | |||||
void setupChannels (const Array<ChannelGroup>&, const Array<Channel>&); | |||||
//========================================================================== | |||||
/** iOS only: sends an asynchronous request to retrieve a list of notifications that were | |||||
scheduled and not yet delivered. | |||||
When the list is retrieved, Listener::pendingLocalNotificationsListReceived() will be called. | |||||
*/ | |||||
void getPendingLocalNotifications() const; | |||||
/** Unschedules a pending local notification with a given identifier. Available from iOS 10. */ | |||||
void removePendingLocalNotification (const String& identifier); | |||||
/** Unschedules all pending local notifications. iOS only. */ | |||||
void removeAllPendingLocalNotifications(); | |||||
//========================================================================== | |||||
/** Checks whether notifications are enabled for given application. | |||||
On iOS this will always return true, use requestSettingsUsed() instead. | |||||
*/ | |||||
bool areNotificationsEnabled() const; | |||||
/** On iOS as well as on Android, sends a local notification. | |||||
On Android and iOS 10 or above, this will refresh an existing notification | |||||
if the same identifier is used as in a notification that was already sent | |||||
and not yet responded by a user. | |||||
*/ | |||||
void sendLocalNotification (const Notification& n); | |||||
/** Sends a request for a list of notifications delivered. Such notifications are visible in the | |||||
notification area on the device and they are still waiting for user action/response. | |||||
When the request is finished Listener::deliveredNotificationsListReceived() will be called. | |||||
On iOS, iOS version 10 or higher is required. On Android, API level 18 or higher is required. | |||||
For unsupported platforms, Listener::deliveredNotificationsListReceived() will return an empty array. | |||||
*/ | |||||
void getDeliveredNotifications() const; | |||||
/** Removes a previously delivered notification. This can be useful for instance when the | |||||
information in the notification becomes obsolete. | |||||
*/ | |||||
void removeDeliveredNotification (const String& identifier); | |||||
/** Removes all notifications that were delivered. */ | |||||
void removeAllDeliveredNotifications(); | |||||
//========================================================================== | |||||
/** Retrieves current device token. Note, it is not a good idea to cache this token | |||||
because it may change in the meantime. Always call this method to get the current | |||||
token value. | |||||
*/ | |||||
String getDeviceToken() const; | |||||
/** Android only: allows to subscribe to messages from a specific topic. | |||||
So you could for instance subscribe this device to all "sports" topic messages | |||||
to receive any remote notifications that have "sports" topic set. | |||||
Refer to Firebase documentation for how to send topic messages. | |||||
*/ | |||||
void subscribeToTopic (const String& topic); | |||||
/** Android only: allows to remove a topic subscription that was previously added with | |||||
subscribeToTopic(). | |||||
*/ | |||||
void unsubscribeFromTopic (const String& topic); | |||||
/** Android only: sends an upstream message to your app server. The server must implement | |||||
XMPP Connection Server protocol (refer to Firebase documentation). | |||||
@param serverSenderId Represents the sender. Consult your Firebase project | |||||
settings to retrieve the sender id. | |||||
@param collapseKey Remote messages with the same collapse key that were not | |||||
yet delivered will be collapsed into one, with the | |||||
newest message replacing all the previous ones. | |||||
Note that there may be a limit of maximum collapse keys | |||||
used at the same time and beyond the limit (refer to | |||||
Firebase documentation) it is not guaranteed which keys | |||||
will be in use by the server. | |||||
@param messageId A unique message ID. Used in error callbacks and debugging. | |||||
@param messageType Message type. | |||||
@param timeToLive TTL in seconds. If 0, the message sending will be attempted | |||||
immediately and it will be dropped if the device is not | |||||
connected. Otherwise, the message will be queued for the | |||||
period specified. | |||||
@param additionalData Collection of key-value pairs to be used as an additional | |||||
data for the message. | |||||
*/ | |||||
void sendUpstreamMessage (const String& serverSenderId, | |||||
const String& collapseKey, | |||||
const String& messageId, | |||||
const String& messageType, | |||||
int timeToLive, | |||||
const StringPairArray& additionalData); | |||||
//========================================================================== | |||||
/** Register a listener (ideally on application startup) to receive information about | |||||
notifications received and any callbacks to async functions called. | |||||
*/ | |||||
struct Listener | |||||
{ | |||||
virtual ~Listener() {} | |||||
/** This callback will be called after you call requestSettingsUsed() or | |||||
requestPermissionsWithSettings(). | |||||
Note that settings are currently only used on iOS. When called on other platforms, Settings | |||||
with no categories and all allow* flags set to true will be received in | |||||
Listener::notificationSettingsReceived(). | |||||
*/ | |||||
virtual void notificationSettingsReceived (const Settings&) {} | |||||
/** Called when the list of pending notifications, requested by calling | |||||
getPendingLocalNotifications() is returned. iOS 10 or above only. | |||||
*/ | |||||
virtual void pendingLocalNotificationsListReceived (const Array<Notification>&) {} | |||||
/** This can be called in multiple different situations, depending on the OS and the situation. | |||||
On pre iOS 10 device it will be called when a user presses on a notification or when a | |||||
notification was received when the app was in the foreground already. On iOS 10 it will be | |||||
called when a user presses on a notification | |||||
Note: on Android, if remote notification was received while the app was in the background and | |||||
then user pressed on it, the notification object received in this callback will contain only | |||||
"properties" member set. Hence, if you want to know what was the notification title, content | |||||
etc, you need to set them as additional properties, so that you will be able to restore them | |||||
from "properties" dictionary. | |||||
Note you can receive this callback on startup, if the application was launched from a notification. | |||||
*/ | |||||
virtual void handleNotification (bool /*isLocalNotification*/, const Notification& /*n*/) {} | |||||
/** This can be called when a user performs some action on the notification such as | |||||
pressing on an action button or responding with a text input. | |||||
Note that pressing on a notification area, i.e. not on an action button is not considered | |||||
to be an action, and hence receivedNotification() will be called in that case. | |||||
Note you can receive this callback on startup, if the application was launched from a notification's action. | |||||
@param optionalResponse Text response a user inputs for notifications with a text input. | |||||
Empty for notifications without a text input option. | |||||
*/ | |||||
virtual void handleNotificationAction (bool /*isLocalNotification*/, | |||||
const Notification& /*n*/, | |||||
const String& /*actionIdentifier*/, | |||||
const String& /*optionalResponse*/) {} | |||||
/** For iOS10 and Android, this can be also called when a user dismissed the notification before | |||||
responding to it. | |||||
*/ | |||||
virtual void localNotificationDismissedByUser (const Notification& /*n*/) {} | |||||
/** Called after getDeliveredNotifications() request is fulfilled. Returns notifications | |||||
that are visible in the notification area on the device and that are still waiting | |||||
for a user action/response. | |||||
On iOS, iOS version 10 or higher is required. On Android, API level 18 or higher is required. | |||||
For unsupported platforms, an empty array will be returned. | |||||
*/ | |||||
virtual void deliveredNotificationsListReceived (const Array<Notification>&) {} | |||||
/** Called whenever a token gets refreshed. You should monitor any token updates, because | |||||
only the last token that is assigned to device is valid and can be used. | |||||
*/ | |||||
virtual void deviceTokenRefreshed (const String& /*token*/) {} | |||||
/** Called when Firebase Cloud Messaging server deletes pending messages. This can happen when | |||||
1) too many messages were sent to the server (hint: use collapsible messages). | |||||
2) the devices hasn't been online in a long time (refer to Firebase documentation for | |||||
the maximum time a message can be stored on FCM before expiring). | |||||
*/ | |||||
virtual void remoteNotificationsDeleted() {} | |||||
/** Called when an upstream message sent with PushNotifications::sendUpstreamMessage() has been | |||||
sent successfully. | |||||
Bear in mind that in may take several minutes or more to receive this callback. | |||||
*/ | |||||
virtual void upstreamMessageSent (const String& /*messageId*/) {} | |||||
/** Called when there was an error sending an upstream message with | |||||
PushNotifications::sendUpstreamMessage(). | |||||
Bear in mind that in may take several minutes or more to receive this callback. | |||||
*/ | |||||
virtual void upstreamMessageSendingError (const String& /*messageId*/, const String& /*error*/) {} | |||||
}; | |||||
void addListener (Listener* l); | |||||
void removeListener (Listener* l); | |||||
private: | |||||
PushNotifications(); | |||||
~PushNotifications(); | |||||
ListenerList<PushNotifications::Listener> listeners; | |||||
#if JUCE_ANDROID | |||||
friend bool juce_handleNotificationIntent (void* intent); | |||||
friend void juce_firebaseDeviceNotificationsTokenRefreshed (void*); | |||||
friend void juce_firebaseRemoteNotificationReceived (void*); | |||||
friend void juce_firebaseRemoteMessagesDeleted(); | |||||
friend void juce_firebaseRemoteMessageSent (void*); | |||||
friend void juce_firebaseRemoteMessageSendError (void*, void*); | |||||
#endif | |||||
#if JUCE_PUSH_NOTIFICATIONS | |||||
struct Pimpl; | |||||
friend struct Pimpl; | |||||
ScopedPointer<Pimpl> pimpl; | |||||
#endif | |||||
}; | |||||
} // namespace juce |