| @@ -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, | |||
| androidExternalReadPermission, androidExternalWritePermission, | |||
| androidInAppBillingPermission; | |||
| androidInAppBillingPermission, androidVibratePermission; | |||
| CachedValue<String> androidOtherPermissions; | |||
| CachedValue<bool> androidEnableRemoteNotifications; | |||
| CachedValue<String> androidRemoteNotificationsConfigFile; | |||
| CachedValue<String> androidKeyStore, androidKeyStorePass, androidKeyAlias, androidKeyAliasPass; | |||
| CachedValue<String> gradleVersion, androidPluginVersion, gradleToolchain, buildToolsVersion; | |||
| @@ -135,7 +138,10 @@ public: | |||
| androidExternalReadPermission (settings, Ids::androidExternalReadNeeded, nullptr, true), | |||
| androidExternalWritePermission (settings, Ids::androidExternalWriteNeeded, nullptr, true), | |||
| androidInAppBillingPermission (settings, Ids::androidInAppBilling, nullptr, false), | |||
| androidVibratePermission (settings, Ids::androidVibratePermissionNeeded, nullptr, false), | |||
| 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"), | |||
| androidKeyStorePass (settings, Ids::androidKeyStorePass, nullptr, "android"), | |||
| androidKeyAlias (settings, Ids::androidKeyAlias, nullptr, "androiddebugkey"), | |||
| @@ -327,6 +333,9 @@ protected: | |||
| Value getAdditionalXmlResourcesValue() { return getValue (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); } | |||
| 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). " | |||
| "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 | |||
| @@ -533,12 +548,24 @@ private: | |||
| mo << " }" << newLine; | |||
| mo << " dependencies {" << 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 << "allprojects {" << newLine; | |||
| mo << " repositories {" << newLine; | |||
| mo << " jcenter()" << newLine; | |||
| if (androidEnableRemoteNotifications.get()) | |||
| { | |||
| mo << " maven {" << newLine; | |||
| mo << " url \"https://maven.google.com\"" << newLine; | |||
| mo << " }" << newLine; | |||
| } | |||
| mo << " }" << newLine; | |||
| mo << "}" << newLine; | |||
| @@ -568,6 +595,7 @@ private: | |||
| mo << getAndroidRepositories() << newLine; | |||
| mo << getAndroidDependencies() << newLine; | |||
| mo << getApplyPlugins() << newLine; | |||
| mo << "}" << newLine << newLine; | |||
| @@ -748,12 +776,28 @@ private: | |||
| juce::StringArray dependencies; | |||
| 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) | |||
| 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(); | |||
| } | |||
| @@ -838,9 +882,19 @@ private: | |||
| 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."); | |||
| 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), | |||
| "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), | |||
| "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 " | |||
| @@ -902,10 +956,11 @@ private: | |||
| auto inAppBillingPath = String ("com.android.vending.billing").replaceCharacter ('.', File::getSeparatorChar()); | |||
| auto javaSourceFolder = coreModule->getFolder().getChildFile ("native").getChildFile ("java"); | |||
| auto javaInAppBillingTarget = targetFolder.getChildFile ("app/src/main/java").getChildFile (inAppBillingPath); | |||
| auto 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); | |||
| } | |||
| } | |||
| @@ -994,21 +1049,69 @@ private: | |||
| 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 | |||
| { | |||
| for (ConstConfigIterator config (*this); config.next();) | |||
| { | |||
| 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)); | |||
| @@ -1016,7 +1119,7 @@ private: | |||
| for (const auto& path : resourcePaths) | |||
| { | |||
| juce::File file (getProject().getFile().getChildFile(path)); | |||
| File file (getProject().getFile().getChildFile (path)); | |||
| jassert (file.existsAsFile()); | |||
| @@ -1076,21 +1179,19 @@ private: | |||
| { | |||
| 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); | |||
| 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 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_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | |||
| defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\"" + getJNIActivityClassName() + "\""); | |||
| defines.set ("JUCE_PUSH_NOTIFICATIONS", "1"); | |||
| if (androidInAppBillingPermission.get()) | |||
| defines.set ("JUCE_IN_APP_PURCHASES", "1"); | |||
| if (androidEnableRemoteNotifications.get()) | |||
| { | |||
| auto instanceIdClassName = getActivityClassPackage() + ".JuceFirebaseInstanceIdService"; | |||
| auto messagingClassName = getActivityClassPackage() + ".JuceFirebaseMessagingService"; | |||
| defines.set ("JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME", instanceIdClassName.replaceCharacter ('.', '_')); | |||
| defines.set ("JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME", messagingClassName.replaceCharacter ('.', '_')); | |||
| } | |||
| if (supportsGLv3()) | |||
| defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1"); | |||
| @@ -1475,6 +1585,7 @@ private: | |||
| } | |||
| setAttributeIfNotPresent (*act, "android:screenOrientation", androidScreenOrientation.get()); | |||
| setAttributeIfNotPresent (*act, "android:launchMode", "singleTask"); | |||
| auto* intent = getOrCreateChildWithName (*act, "intent-filter"); | |||
| @@ -1483,6 +1594,23 @@ private: | |||
| auto* category = getOrCreateChildWithName (*intent, "category"); | |||
| 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; | |||
| @@ -1531,6 +1659,9 @@ private: | |||
| if (androidInAppBillingPermission.get()) | |||
| s.add ("com.android.vending.BILLING"); | |||
| if (androidVibratePermission.get()) | |||
| s.add ("android.permission.VIBRATE"); | |||
| return getCleanedStringArray (s); | |||
| } | |||
| @@ -1134,6 +1134,9 @@ public: | |||
| if (owner.isInAppPurchasesEnabled()) | |||
| 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)); | |||
| StringArray defsList; | |||
| @@ -2243,6 +2246,9 @@ private: | |||
| if (isInAppPurchasesEnabled()) | |||
| xcodeFrameworks.addIfNotAlreadyThere ("StoreKit"); | |||
| if (iOS && isPushNotificationsEnabled()) | |||
| xcodeFrameworks.addIfNotAlreadyThere ("UserNotifications"); | |||
| xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'"); | |||
| xcodeFrameworks.trim(); | |||
| @@ -171,6 +171,7 @@ namespace Ids | |||
| DECLARE_ID (androidRepositories); | |||
| DECLARE_ID (androidDependencies); | |||
| DECLARE_ID (androidAdditionalXmlValueResources); | |||
| DECLARE_ID (androidAdditionalRawValueResources); | |||
| DECLARE_ID (androidActivityClass); | |||
| DECLARE_ID (androidActivitySubClassName); | |||
| DECLARE_ID (androidVersionCode); | |||
| @@ -184,6 +185,9 @@ namespace Ids | |||
| DECLARE_ID (androidExternalReadNeeded); | |||
| DECLARE_ID (androidExternalWriteNeeded); | |||
| DECLARE_ID (androidInAppBilling); | |||
| DECLARE_ID (androidVibratePermissionNeeded); | |||
| DECLARE_ID (androidEnableRemoteNotifications); | |||
| DECLARE_ID (androidRemoteNotificationsConfigFile); | |||
| DECLARE_ID (androidMinimumSDK); | |||
| DECLARE_ID (androidOtherPermissions); | |||
| DECLARE_ID (androidKeyStore); | |||
| @@ -171,6 +171,10 @@ | |||
| #include "native/juce_BasicNativeHeaders.h" | |||
| #endif | |||
| #if JUCE_WINDOWS | |||
| #undef small | |||
| #endif | |||
| #include "system/juce_StandardHeader.h" | |||
| namespace juce | |||
| @@ -229,6 +229,7 @@ public class JuceAppActivity extends Activity | |||
| getApplicationInfo().dataDir); | |||
| } | |||
| //============================================================================== | |||
| private void hideActionBar() | |||
| { | |||
| // get "getActionBar" method | |||
| @@ -300,6 +301,7 @@ public class JuceAppActivity extends Activity | |||
| private native void resumeApp(); | |||
| private native void setScreenSize (int screenWidth, int screenHeight, int dpi); | |||
| private native void appActivityResult (int requestCode, int resultCode, Intent data); | |||
| private native void appNewIntent (Intent intent); | |||
| //============================================================================== | |||
| private ViewHolder viewHolder; | |||
| @@ -1367,6 +1369,15 @@ public class JuceAppActivity extends Activity | |||
| appActivityResult (requestCode, resultCode, data); | |||
| } | |||
| @Override | |||
| protected void onNewIntent (Intent intent) | |||
| { | |||
| super.onNewIntent(intent); | |||
| setIntent(intent); | |||
| appNewIntent (intent); | |||
| } | |||
| //============================================================================== | |||
| 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) | |||
| { | |||
| jobject newObj = retain (other.obj); | |||
| JavaType newObj = retain (other.obj); | |||
| clear(); | |||
| obj = newObj; | |||
| return *this; | |||
| @@ -168,6 +168,9 @@ namespace | |||
| { | |||
| inline String juceString (JNIEnv* env, jstring s) | |||
| { | |||
| if (s == 0) | |||
| return {}; | |||
| const char* const utf8 = env->GetStringUTFChars (s, nullptr); | |||
| CharPointer_UTF8 utf8CP (utf8); | |||
| const String result (utf8CP); | |||
| @@ -313,11 +316,12 @@ extern AndroidSystem android; | |||
| METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ | |||
| METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ | |||
| 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;") \ | |||
| METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ | |||
| METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ | |||
| METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ | |||
| METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ | |||
| METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \ | |||
| METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ | |||
| @@ -399,15 +403,20 @@ DECLARE_JNI_CLASS (JavaObject, "java/lang/Object"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #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"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -53,6 +53,11 @@ | |||
| #import <Carbon/Carbon.h> // still needed for SetSystemUIMode() | |||
| #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 | |||
| #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 | |||
| extern void juce_inAppPurchaseCompleted (void*); | |||
| #endif | |||
| @@ -98,6 +108,75 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, appActivityResult, void, (JN | |||
| #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) \ | |||
| METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \ | |||
| @@ -38,7 +38,11 @@ namespace juce | |||
| 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> | |||
| #endif | |||
| { | |||
| UIBackgroundTaskIdentifier appSuspendTask; | |||
| } | |||
| @@ -51,17 +55,45 @@ namespace juce | |||
| - (void) applicationWillEnterForeground: (UIApplication*) application; | |||
| - (void) applicationDidBecomeActive: (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 | |||
| @implementation JuceAppStartupDelegate | |||
| NSObject* _pushNotificationsDelegate; | |||
| - (id)init | |||
| { | |||
| self = [super init]; | |||
| 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; | |||
| } | |||
| @@ -118,7 +150,8 @@ namespace juce | |||
| - (void) applicationDidBecomeActive: (UIApplication*) application | |||
| { | |||
| ignoreUnused (application); | |||
| application.applicationIconBadgeNumber = 0; | |||
| isIOSAppActive = true; | |||
| } | |||
| @@ -139,6 +172,230 @@ namespace juce | |||
| 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 | |||
| namespace juce | |||
| @@ -35,10 +35,15 @@ | |||
| #define JUCE_CORE_INCLUDE_OBJC_HELPERS 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_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | |||
| #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | |||
| #ifndef JUCE_PUSH_NOTIFICATIONS | |||
| #define JUCE_PUSH_NOTIFICATIONS 0 | |||
| #endif | |||
| #include "juce_gui_extra.h" | |||
| //============================================================================== | |||
| @@ -50,7 +55,21 @@ | |||
| #import <IOKit/hid/IOHIDKeys.h> | |||
| #import <IOKit/pwr_mgt/IOPMLib.h> | |||
| //============================================================================== | |||
| #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 | |||
| @@ -93,6 +112,7 @@ | |||
| #include "misc/juce_ColourSelector.cpp" | |||
| #include "misc/juce_KeyMappingEditorComponent.cpp" | |||
| #include "misc/juce_PreferencesPanel.cpp" | |||
| #include "misc/juce_PushNotifications.cpp" | |||
| #include "misc/juce_RecentlyOpenedFilesList.cpp" | |||
| #include "misc/juce_SplashScreen.cpp" | |||
| #include "misc/juce_SystemTrayIconComponent.cpp" | |||
| @@ -91,6 +91,7 @@ | |||
| #include "misc/juce_ColourSelector.h" | |||
| #include "misc/juce_KeyMappingEditorComponent.h" | |||
| #include "misc/juce_PreferencesPanel.h" | |||
| #include "misc/juce_PushNotifications.h" | |||
| #include "misc/juce_RecentlyOpenedFilesList.h" | |||
| #include "misc/juce_SplashScreen.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 | |||