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