Browse Source

Add Push Notifications feature for iOS and Android.

tags/2021-05-28
Lukasz Kozakiewicz 7 years ago
parent
commit
b2d2bda7a1
71 changed files with 16041 additions and 37 deletions
  1. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name.png
  2. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name10.png
  3. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name2.png
  4. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name3.png
  5. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name4.png
  6. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name5.png
  7. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name6.png
  8. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name7.png
  9. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name8.png
  10. BIN
      examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name9.png
  11. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/demonstrative.caf
  12. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/demonstrative.mp3
  13. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/isntit.caf
  14. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/isntit.mp3
  15. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/jinglebellssms.caf
  16. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/jinglebellssms.mp3
  17. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/served.caf
  18. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/served.mp3
  19. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/solemn.caf
  20. BIN
      examples/PushNotificationsDemo/BinaryResources/sounds/solemn.mp3
  21. +228
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/AppConfig.h
  22. +9501
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/BinaryData.cpp
  23. +80
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/BinaryData.h
  24. +45
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/JuceHeader.h
  25. +12
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/ReadMe.txt
  26. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_basics.cpp
  27. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_basics.mm
  28. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_devices.cpp
  29. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_devices.mm
  30. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_formats.cpp
  31. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_formats.mm
  32. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_processors.cpp
  33. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_processors.mm
  34. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_core.cpp
  35. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_core.mm
  36. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_cryptography.cpp
  37. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_cryptography.mm
  38. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_data_structures.cpp
  39. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_data_structures.mm
  40. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_events.cpp
  41. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_events.mm
  42. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_graphics.cpp
  43. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_graphics.mm
  44. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_basics.cpp
  45. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_basics.mm
  46. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_extra.cpp
  47. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_extra.mm
  48. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_opengl.cpp
  49. +9
    -0
      examples/PushNotificationsDemo/JuceLibraryCode/include_juce_opengl.mm
  50. +161
    -0
      examples/PushNotificationsDemo/PushNotificationsDemo.jucer
  51. +96
    -0
      examples/PushNotificationsDemo/Source/Main.cpp
  52. +628
    -0
      examples/PushNotificationsDemo/Source/MainComponent.cpp
  53. +658
    -0
      examples/PushNotificationsDemo/Source/MainComponent.h
  54. +42
    -0
      examples/PushNotificationsDemo/google-services.json
  55. +154
    -23
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h
  56. +6
    -0
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
  57. +4
    -0
      extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
  58. +4
    -0
      modules/juce_core/juce_core.h
  59. +11
    -0
      modules/juce_core/native/java/JuceAppActivity.java
  60. +16
    -0
      modules/juce_core/native/java/JuceFirebaseInstanceIdService.java
  61. +35
    -0
      modules/juce_core/native/java/JuceFirebaseMessagingService.java
  62. +20
    -11
      modules/juce_core/native/juce_android_JNIHelpers.h
  63. +5
    -0
      modules/juce_gui_basics/juce_gui_basics.cpp
  64. +79
    -0
      modules/juce_gui_basics/native/juce_android_Windowing.cpp
  65. +260
    -3
      modules/juce_gui_basics/native/juce_ios_Windowing.mm
  66. +20
    -0
      modules/juce_gui_extra/juce_gui_extra.cpp
  67. +1
    -0
      modules/juce_gui_extra/juce_gui_extra.h
  68. +190
    -0
      modules/juce_gui_extra/misc/juce_PushNotifications.cpp
  69. +687
    -0
      modules/juce_gui_extra/misc/juce_PushNotifications.h
  70. +1761
    -0
      modules/juce_gui_extra/native/juce_android_PushNotifications.cpp
  71. +1121
    -0
      modules/juce_gui_extra/native/juce_ios_PushNotifications.cpp

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name.png View File

Before After
Width: 24  |  Height: 24  |  Size: 351B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name10.png View File

Before After
Width: 48  |  Height: 75  |  Size: 1.1KB

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name2.png View File

Before After
Width: 24  |  Height: 24  |  Size: 204B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name3.png View File

Before After
Width: 24  |  Height: 24  |  Size: 292B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name4.png View File

Before After
Width: 24  |  Height: 24  |  Size: 341B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name5.png View File

Before After
Width: 24  |  Height: 24  |  Size: 337B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name6.png View File

Before After
Width: 48  |  Height: 75  |  Size: 1.0KB

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name7.png View File

Before After
Width: 48  |  Height: 75  |  Size: 543B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name8.png View File

Before After
Width: 48  |  Height: 75  |  Size: 928B

BIN
examples/PushNotificationsDemo/BinaryResources/images/ic_stat_name9.png View File

Before After
Width: 48  |  Height: 75  |  Size: 915B

BIN
examples/PushNotificationsDemo/BinaryResources/sounds/demonstrative.caf View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/demonstrative.mp3 View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/isntit.caf View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/isntit.mp3 View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/jinglebellssms.caf View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/jinglebellssms.mp3 View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/served.caf View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/served.mp3 View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/solemn.caf View File


BIN
examples/PushNotificationsDemo/BinaryResources/sounds/solemn.mp3 View File


+ 228
- 0
examples/PushNotificationsDemo/JuceLibraryCode/AppConfig.h View File

@@ -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

+ 9501
- 0
examples/PushNotificationsDemo/JuceLibraryCode/BinaryData.cpp
File diff suppressed because it is too large
View File


+ 80
- 0
examples/PushNotificationsDemo/JuceLibraryCode/BinaryData.h View File

@@ -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();
}

+ 45
- 0
examples/PushNotificationsDemo/JuceLibraryCode/JuceHeader.h View File

@@ -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

+ 12
- 0
examples/PushNotificationsDemo/JuceLibraryCode/ReadMe.txt View File

@@ -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).

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_basics.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_basics.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_devices.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_devices.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_formats.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_formats.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_processors.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_audio_processors.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_core.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_core.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_cryptography.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_cryptography.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_data_structures.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_data_structures.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_events.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_events.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_graphics.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_graphics.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_basics.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_basics.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_extra.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_gui_extra.mm View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_opengl.cpp View File

@@ -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>

+ 9
- 0
examples/PushNotificationsDemo/JuceLibraryCode/include_juce_opengl.mm View File

@@ -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>

+ 161
- 0
examples/PushNotificationsDemo/PushNotificationsDemo.jucer View File

@@ -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&#10;-F../../../../3rd_party/FacebookSDKs-iOS-4&#10;-F../../../../3rd_party/Firebase_ios_sdk/Analytics&#10;-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&#10;-F../../../../3rd_party/FacebookSDKs-iOS-4&#10;-F../../../../3rd_party/Firebase_ios_sdk/Analytics&#10;-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&#10;../BinaryResources/sounds/isntit.mp3&#10;../BinaryResources/sounds/jinglebellssms.mp3&#10;../BinaryResources/sounds/served.mp3&#10;../BinaryResources/sounds/solemn.mp3&#10;../BinaryResources/images/ic_stat_name.png&#10;../BinaryResources/images/ic_stat_name2.png&#10;../BinaryResources/images/ic_stat_name3.png&#10;../BinaryResources/images/ic_stat_name4.png&#10;../BinaryResources/images/ic_stat_name5.png&#10;../BinaryResources/images/ic_stat_name6.png&#10;../BinaryResources/images/ic_stat_name7.png&#10;../BinaryResources/images/ic_stat_name8.png&#10;../BinaryResources/images/ic_stat_name9.png&#10;../BinaryResources/images/ic_stat_name10.png"/>
<CONFIGURATION name="Release" androidArchitectures="" isDebug="0" optimisation="3"
targetName="PushNotificationsDemo" androidAdditionalRawValueResources="../BinaryResources/sounds/demonstrative.mp3&#10;../BinaryResources/sounds/isntit.mp3&#10;../BinaryResources/sounds/jinglebellssms.mp3&#10;../BinaryResources/sounds/served.mp3&#10;../BinaryResources/sounds/solemn.mp3&#10;../BinaryResources/images/ic_stat_name.png&#10;../BinaryResources/images/ic_stat_name2.png&#10;../BinaryResources/images/ic_stat_name3.png&#10;../BinaryResources/images/ic_stat_name4.png&#10;../BinaryResources/images/ic_stat_name5.png&#10;../BinaryResources/images/ic_stat_name6.png&#10;../BinaryResources/images/ic_stat_name7.png&#10;../BinaryResources/images/ic_stat_name8.png&#10;../BinaryResources/images/ic_stat_name9.png&#10;../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>

+ 96
- 0
examples/PushNotificationsDemo/Source/Main.cpp View File

@@ -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)

+ 628
- 0
examples/PushNotificationsDemo/Source/MainComponent.cpp View File

@@ -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

+ 658
- 0
examples/PushNotificationsDemo/Source/MainComponent.h View File

@@ -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)
};

+ 42
- 0
examples/PushNotificationsDemo/google-services.json View File

@@ -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"
}

+ 154
- 23
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h View File

@@ -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);
}


+ 6
- 0
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h View File

@@ -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();


+ 4
- 0
extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h View File

@@ -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);


+ 4
- 0
modules/juce_core/juce_core.h View File

@@ -171,6 +171,10 @@
#include "native/juce_BasicNativeHeaders.h"
#endif
#if JUCE_WINDOWS
#undef small
#endif
#include "system/juce_StandardHeader.h"
namespace juce


+ 11
- 0
modules/juce_core/native/java/JuceAppActivity.java View File

@@ -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)
{


+ 16
- 0
modules/juce_core/native/java/JuceFirebaseInstanceIdService.java View File

@@ -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);
}
}

+ 35
- 0
modules/juce_core/native/java/JuceFirebaseMessagingService.java View File

@@ -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());
}
}

+ 20
- 11
modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -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


+ 5
- 0
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -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>


+ 79
- 0
modules/juce_gui_basics/native/juce_android_Windowing.cpp View File

@@ -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") \


+ 260
- 3
modules/juce_gui_basics/native/juce_ios_Windowing.mm View File

@@ -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: &notificationSettings 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: &notification 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: &notification 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: &notification 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: &center atIndex:2];
[invocation setArgument: &notification 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: &center atIndex:2];
[invocation setArgument: &response atIndex:3];
[invocation setArgument: &completionHandler atIndex:4];
[invocation invoke];
}
}
#endif
@end
namespace juce


+ 20
- 0
modules/juce_gui_extra/juce_gui_extra.cpp View File

@@ -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"


+ 1
- 0
modules/juce_gui_extra/juce_gui_extra.h View File

@@ -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"


+ 190
- 0
modules/juce_gui_extra/misc/juce_PushNotifications.cpp View File

@@ -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

+ 687
- 0
modules/juce_gui_extra/misc/juce_PushNotifications.h View File

@@ -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

+ 1761
- 0
modules/juce_gui_extra/native/juce_android_PushNotifications.cpp
File diff suppressed because it is too large
View File


+ 1121
- 0
modules/juce_gui_extra/native/juce_ios_PushNotifications.cpp
File diff suppressed because it is too large
View File


Loading…
Cancel
Save