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