The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1263 lines
58KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: PushNotificationsDemo
  20. version: 2.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Showcases push notifications features. To run this demo you must enable the
  24. "Push Notifications Capability" option in the Projucer exporter.
  25. dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
  26. juce_audio_processors, juce_audio_utils, juce_core,
  27. juce_data_structures, juce_events, juce_graphics,
  28. juce_gui_basics, juce_gui_extra
  29. exporters: xcode_mac, xcode_iphone, androidstudio
  30. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  31. JUCE_PUSH_NOTIFICATIONS=1
  32. type: Component
  33. mainClass: PushNotificationsDemo
  34. useLocalCopy: 1
  35. END_JUCE_PIP_METADATA
  36. *******************************************************************************/
  37. #pragma once
  38. #include "../Assets/DemoUtilities.h"
  39. /*
  40. To finish the setup of this demo, do the following:
  41. 1. Download google_services.json from your Firebase project.
  42. 2. Update "Remote Notifications Config File" path in Android exporter (this can be different for debug and release)
  43. to point to that json file.
  44. 3. Add image and sound resources by adding the following to "Extra Android Raw Resources" in Projucer:
  45. ../../Assets/Notifications/images/ic_stat_name.png
  46. ../../Assets/Notifications/images/ic_stat_name2.png
  47. ../../Assets/Notifications/images/ic_stat_name3.png
  48. ../../Assets/Notifications/images/ic_stat_name4.png
  49. ../../Assets/Notifications/images/ic_stat_name5.png
  50. ../../Assets/Notifications/images/ic_stat_name6.png
  51. ../../Assets/Notifications/images/ic_stat_name7.png
  52. ../../Assets/Notifications/images/ic_stat_name8.png
  53. ../../Assets/Notifications/images/ic_stat_name9.png
  54. ../../Assets/Notifications/images/ic_stat_name10.png
  55. ../../Assets/Notifications/sounds/demonstrative.mp3
  56. ../../Assets/Notifications/sounds/isntit.mp3
  57. ../../Assets/Notifications/sounds/jinglebellssms.mp3
  58. ../../Assets/Notifications/sounds/served.mp3
  59. ../../Assets/Notifications/sounds/solemn.mp3
  60. 4. Set "Remote Notifications" to enabled in Projucer Android exporter.
  61. To verify that remote notifications are configured properly, go to Remote tab in the demo and press "GetDeviceToken"
  62. button, a dialog with your token (also printed to console in debug build) should show up.
  63. The following steps are only necessary if you have a custom activity defined:
  64. 5. Ensure that its launchMode is set to "singleTop" or "singleTask" in Android manifest. This is the default behaviour
  65. in JUCE so you only need to do it if you have custom Android manifest content. You can do it from Projucer by
  66. ensuring that "Custom Manifest XML Content" contains:
  67. <manifest>
  68. <application>
  69. <activity android:launchMode="singleTask">
  70. </activity>
  71. </application>
  72. </manifest>
  73. 6. Ensure that you override onNewIntent() function in the same way as it is done in JuceActivity.java:
  74. package com.rmsl.juce;
  75. import android.app.Activity;
  76. import android.content.Intent;
  77. //==============================================================================
  78. public class JuceActivity extends Activity
  79. {
  80. //==============================================================================
  81. private native void appNewIntent (Intent intent);
  82. @Override
  83. protected void onNewIntent (Intent intent)
  84. {
  85. super.onNewIntent (intent);
  86. setIntent (intent);
  87. appNewIntent (intent);
  88. }
  89. }
  90. */
  91. //==============================================================================
  92. class PushNotificationsDemo final : public Component,
  93. private ChangeListener,
  94. private ComponentListener,
  95. private PushNotifications::Listener
  96. {
  97. public:
  98. //==============================================================================
  99. PushNotificationsDemo()
  100. {
  101. setupControls();
  102. distributeControls();
  103. #if JUCE_PUSH_NOTIFICATIONS
  104. addAndMakeVisible (headerLabel);
  105. addAndMakeVisible (mainTabs);
  106. addAndMakeVisible (sendButton);
  107. #else
  108. addAndMakeVisible (notAvailableYetLabel);
  109. #endif
  110. headerLabel .setJustificationType (Justification::centred);
  111. notAvailableYetLabel.setJustificationType (Justification::centred);
  112. #if JUCE_MAC
  113. StringArray tabNames { "Params1", "Params2", "Params3", "Params4" };
  114. #else
  115. StringArray tabNames { "Req. params", "Opt. params1", "Opt. params2", "Opt. params3" };
  116. #endif
  117. auto colour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
  118. localNotificationsTabs.addTab (tabNames[0], colour, &paramsOneView, false);
  119. localNotificationsTabs.addTab (tabNames[1], colour, &paramsTwoView, false);
  120. #if JUCE_ANDROID
  121. localNotificationsTabs.addTab (tabNames[2], colour, &paramsThreeView, false);
  122. localNotificationsTabs.addTab (tabNames[3], colour, &paramsFourView, false);
  123. #endif
  124. localNotificationsTabs.addTab ("Aux. actions", colour, &auxActionsView, false);
  125. mainTabs.addTab ("Local", colour, &localNotificationsTabs, false);
  126. mainTabs.addTab ("Remote", colour, &remoteView, false);
  127. auto userArea = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
  128. #if JUCE_ANDROID || JUCE_IOS
  129. setSize (userArea.getWidth(), userArea.getHeight());
  130. #else
  131. setSize (userArea.getWidth() / 2, userArea.getHeight() / 2);
  132. #endif
  133. sendButton.onClick = [this] { sendLocalNotification(); };
  134. auxActionsView.getDeliveredNotificationsButton .onClick = []
  135. { PushNotifications::getInstance()->getDeliveredNotifications(); };
  136. auxActionsView.removeDeliveredNotifWithIdButton.onClick = [this]
  137. { PushNotifications::getInstance()->removeDeliveredNotification (auxActionsView.deliveredNotifIdentifier.getText()); };
  138. auxActionsView.removeAllDeliveredNotifsButton .onClick = []
  139. { PushNotifications::getInstance()->removeAllDeliveredNotifications(); };
  140. #if JUCE_IOS || JUCE_MAC
  141. auxActionsView.getPendingNotificationsButton .onClick = []
  142. { PushNotifications::getInstance()->getPendingLocalNotifications(); };
  143. auxActionsView.removePendingNotifWithIdButton.onClick = [this]
  144. { PushNotifications::getInstance()->removePendingLocalNotification (auxActionsView.pendingNotifIdentifier.getText()); };
  145. auxActionsView.removeAllPendingNotifsButton .onClick = []
  146. { PushNotifications::getInstance()->removeAllPendingLocalNotifications(); };
  147. #endif
  148. remoteView.getDeviceTokenButton.onClick = [this]
  149. {
  150. String token = PushNotifications::getInstance()->getDeviceToken();
  151. DBG ("token = " + token);
  152. if (token.isEmpty())
  153. {
  154. showRemoteInstructions();
  155. }
  156. else
  157. {
  158. auto options = MessageBoxOptions()
  159. .withIconType (MessageBoxIconType::InfoIcon)
  160. .withTitle ("Device token")
  161. .withMessage (token)
  162. .withButton ("OK");
  163. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  164. }
  165. };
  166. #if JUCE_ANDROID
  167. remoteView.sendRemoteMessageButton.onClick = []
  168. {
  169. StringPairArray data;
  170. data.set ("key1", "value1");
  171. data.set ("key2", "value2");
  172. static int id = 100;
  173. PushNotifications::getInstance()->sendUpstreamMessage ("872047750958",
  174. "com.juce.pushnotificationsdemo",
  175. String (id++),
  176. "standardType",
  177. 3600,
  178. data);
  179. };
  180. remoteView.subscribeToSportsButton .onClick = []
  181. { PushNotifications::getInstance()->subscribeToTopic ("sports"); };
  182. remoteView.unsubscribeFromSportsButton.onClick = []
  183. { PushNotifications::getInstance()->unsubscribeFromTopic ("sports"); };
  184. #endif
  185. paramControls.accentColourButton.onClick = [this] { setupAccentColour(); };
  186. paramControls.ledColourButton .onClick = [this] { setupLedColour(); };
  187. jassert (PushNotifications::getInstance()->areNotificationsEnabled());
  188. PushNotifications::getInstance()->addListener (this);
  189. #if JUCE_IOS || JUCE_MAC
  190. paramControls.fireInComboBox.onChange = [this] { delayNotification(); };
  191. PushNotifications::getInstance()->requestPermissionsWithSettings (getNotificationSettings());
  192. #elif JUCE_ANDROID
  193. PushNotifications::ChannelGroup cg { "demoGroup", "demo group" };
  194. PushNotifications::getInstance()->setupChannels ({ { cg } }, getAndroidChannels());
  195. #endif
  196. #if JUCE_IOS || JUCE_ANDROID
  197. setPortraitOrientationEnabled (true);
  198. #endif
  199. }
  200. ~PushNotificationsDemo() override
  201. {
  202. PushNotifications::getInstance()->removeListener (this);
  203. #if JUCE_IOS || JUCE_ANDROID
  204. setPortraitOrientationEnabled (false);
  205. #endif
  206. }
  207. void setPortraitOrientationEnabled (bool shouldBeEnabled)
  208. {
  209. auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
  210. if (shouldBeEnabled)
  211. allowedOrientations |= Desktop::upright;
  212. else
  213. allowedOrientations &= ~Desktop::upright;
  214. Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
  215. }
  216. void paint (Graphics& g) override
  217. {
  218. g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
  219. }
  220. void resized() override
  221. {
  222. auto bounds = getLocalBounds().reduced (getWidth() / 20, getHeight() / 40);
  223. headerLabel.setBounds (bounds.removeFromTop (bounds.proportionOfHeight (0.1f)));
  224. mainTabs .setBounds (bounds.removeFromTop (bounds.proportionOfHeight (0.8f)));
  225. sendButton .setBounds (bounds);
  226. notAvailableYetLabel.setBounds (getLocalBounds());
  227. }
  228. private:
  229. void delayNotification()
  230. {
  231. auto repeatsAllowed = paramControls.fireInComboBox.getSelectedItemIndex() >= 6;
  232. paramControls.repeatButton.setEnabled (repeatsAllowed);
  233. if (! repeatsAllowed)
  234. paramControls.repeatButton.setToggleState (false, NotificationType::sendNotification);
  235. }
  236. void sendLocalNotification()
  237. {
  238. PushNotifications::Notification n;
  239. fillRequiredParams (n);
  240. fillOptionalParamsOne (n);
  241. #if JUCE_ANDROID
  242. fillOptionalParamsTwo (n);
  243. fillOptionalParamsThree (n);
  244. #endif
  245. if (! n.isValid())
  246. {
  247. #if JUCE_IOS
  248. String requiredFields = "identifier (from iOS 10), title, body and category";
  249. #elif JUCE_ANDROID
  250. String requiredFields = "channel ID (from Android O), title, body and icon";
  251. #else
  252. String requiredFields = "all required fields";
  253. #endif
  254. auto options = MessageBoxOptions()
  255. .withIconType (MessageBoxIconType::InfoIcon)
  256. .withTitle ("Incorrect notifications setup")
  257. .withMessage ("Please make sure that " + requiredFields + " are set.")
  258. .withButton ("OK");
  259. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  260. return;
  261. }
  262. PushNotifications::getInstance()->sendLocalNotification (n);
  263. }
  264. void fillRequiredParams (PushNotifications::Notification& n)
  265. {
  266. n.identifier = paramControls.identifierEditor.getText();
  267. n.title = paramControls.titleEditor .getText();
  268. n.body = paramControls.bodyEditor .getText();
  269. #if JUCE_IOS
  270. n.category = paramControls.categoryComboBox.getText();
  271. #elif JUCE_ANDROID || JUCE_MAC
  272. #if JUCE_MAC
  273. String prefix = "Notifications/images/";
  274. String extension = ".png";
  275. #else
  276. String prefix;
  277. String extension;
  278. #endif
  279. if (paramControls.iconComboBox.getSelectedItemIndex() == 0)
  280. n.icon = prefix + "ic_stat_name" + extension;
  281. else if (paramControls.iconComboBox.getSelectedItemIndex() == 1)
  282. n.icon = prefix + "ic_stat_name2" + extension;
  283. else if (paramControls.iconComboBox.getSelectedItemIndex() == 2)
  284. n.icon = prefix + "ic_stat_name3" + extension;
  285. else if (paramControls.iconComboBox.getSelectedItemIndex() == 3)
  286. n.icon = prefix + "ic_stat_name4" + extension;
  287. else if (paramControls.iconComboBox.getSelectedItemIndex() == 4)
  288. n.icon = prefix + "ic_stat_name5" + extension;
  289. #endif
  290. #if JUCE_ANDROID
  291. // Note: this is not strictly speaking required param, just doing it here because it is the fastest way!
  292. n.publicVersion.reset (new PushNotifications::Notification());
  293. n.publicVersion->identifier = "blahblahblah";
  294. n.publicVersion->title = "Public title!";
  295. n.publicVersion->body = "Public body!";
  296. n.publicVersion->icon = n.icon;
  297. n.channelId = String (paramControls.channelIdComboBox.getSelectedItemIndex() + 1);
  298. #endif
  299. }
  300. void fillOptionalParamsOne (PushNotifications::Notification& n)
  301. {
  302. n.subtitle = paramControls.subtitleEditor.getText();
  303. n.badgeNumber = paramControls.badgeNumberComboBox.getSelectedItemIndex();
  304. if (paramControls.soundToPlayComboBox.getSelectedItemIndex() > 0)
  305. n.soundToPlay = URL (paramControls.soundToPlayComboBox.getItemText (paramControls.soundToPlayComboBox.getSelectedItemIndex()));
  306. n.properties = JSON::parse (paramControls.propertiesEditor.getText());
  307. #if JUCE_IOS || JUCE_MAC
  308. n.triggerIntervalSec = double (paramControls.fireInComboBox.getSelectedItemIndex() * 10);
  309. n.repeat = paramControls.repeatButton.getToggleState();
  310. #elif JUCE_ANDROID
  311. if (paramControls.largeIconComboBox.getSelectedItemIndex() == 1)
  312. n.largeIcon = getImageFromAssets ("Notifications/images/ic_stat_name6.png");
  313. else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 2)
  314. n.largeIcon = getImageFromAssets ("Notifications/images/ic_stat_name7.png");
  315. else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 3)
  316. n.largeIcon = getImageFromAssets ("Notifications/images/ic_stat_name8.png");
  317. else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 4)
  318. n.largeIcon = getImageFromAssets ("Notifications/images/ic_stat_name9.png");
  319. else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 5)
  320. n.largeIcon = getImageFromAssets ("Notifications/images/ic_stat_name10.png");
  321. n.badgeIconType = (PushNotifications::Notification::BadgeIconType) paramControls.badgeIconComboBox.getSelectedItemIndex();
  322. n.tickerText = paramControls.tickerTextEditor.getText();
  323. n.shouldAutoCancel = paramControls.autoCancelButton .getToggleState();
  324. n.alertOnlyOnce = paramControls.alertOnlyOnceButton.getToggleState();
  325. #endif
  326. #if JUCE_ANDROID || JUCE_MAC
  327. if (paramControls.actionsComboBox.getSelectedItemIndex() == 1)
  328. {
  329. PushNotifications::Notification::Action a, a2;
  330. a .style = PushNotifications::Notification::Action::button;
  331. a2.style = PushNotifications::Notification::Action::button;
  332. a .title = a .identifier = "Ok";
  333. a2.title = a2.identifier = "Cancel";
  334. n.actions.add (a);
  335. n.actions.add (a2);
  336. }
  337. else if (paramControls.actionsComboBox.getSelectedItemIndex() == 2)
  338. {
  339. PushNotifications::Notification::Action a, a2;
  340. a .title = a .identifier = "Input Text Here";
  341. a2.title = a2.identifier = "No";
  342. a .style = PushNotifications::Notification::Action::text;
  343. a2.style = PushNotifications::Notification::Action::button;
  344. a .icon = "ic_stat_name4";
  345. a2.icon = "ic_stat_name5";
  346. a.textInputPlaceholder = "placeholder text ...";
  347. n.actions.add (a);
  348. n.actions.add (a2);
  349. }
  350. else if (paramControls.actionsComboBox.getSelectedItemIndex() == 3)
  351. {
  352. PushNotifications::Notification::Action a, a2;
  353. a .title = a .identifier = "Ok";
  354. a2.title = a2.identifier = "Cancel";
  355. a .style = PushNotifications::Notification::Action::button;
  356. a2.style = PushNotifications::Notification::Action::button;
  357. a .icon = "ic_stat_name4";
  358. a2.icon = "ic_stat_name5";
  359. n.actions.add (a);
  360. n.actions.add (a2);
  361. }
  362. else if (paramControls.actionsComboBox.getSelectedItemIndex() == 4)
  363. {
  364. PushNotifications::Notification::Action a, a2;
  365. a .title = a .identifier = "Input Text Here";
  366. a2.title = a2.identifier = "No";
  367. a .style = PushNotifications::Notification::Action::text;
  368. a2.style = PushNotifications::Notification::Action::button;
  369. a .icon = "ic_stat_name4";
  370. a2.icon = "ic_stat_name5";
  371. a.textInputPlaceholder = "placeholder text ...";
  372. a.allowedResponses.add ("Response 1");
  373. a.allowedResponses.add ("Response 2");
  374. a.allowedResponses.add ("Response 3");
  375. n.actions.add (a);
  376. n.actions.add (a2);
  377. }
  378. #endif
  379. }
  380. void fillOptionalParamsTwo (PushNotifications::Notification& n)
  381. {
  382. using Notification = PushNotifications::Notification;
  383. Notification::Progress progress;
  384. progress.max = paramControls.progressMaxComboBox .getSelectedItemIndex() * 10;
  385. progress.current = paramControls.progressCurrentComboBox.getSelectedItemIndex() * 10;
  386. progress.indeterminate = paramControls.progressIndeterminateButton.getToggleState();
  387. n.progress = progress;
  388. n.person = paramControls.personEditor.getText();
  389. n.type = Notification::Type (paramControls.categoryComboBox .getSelectedItemIndex());
  390. n.priority = Notification::Priority (paramControls.priorityComboBox .getSelectedItemIndex() - 2);
  391. n.lockScreenAppearance = Notification::LockScreenAppearance (paramControls.lockScreenVisibilityComboBox.getSelectedItemIndex() - 1);
  392. n.groupId = paramControls.groupIdEditor.getText();
  393. n.groupSortKey = paramControls.sortKeyEditor.getText();
  394. n.groupSummary = paramControls.groupSummaryButton.getToggleState();
  395. n.groupAlertBehaviour = Notification::GroupAlertBehaviour (paramControls.groupAlertBehaviourComboBox.getSelectedItemIndex());
  396. }
  397. void fillOptionalParamsThree (PushNotifications::Notification& n)
  398. {
  399. n.accentColour = paramControls.accentColourButton.findColour (TextButton::buttonColourId, false);
  400. n.ledColour = paramControls.ledColourButton .findColour (TextButton::buttonColourId, false);
  401. using Notification = PushNotifications::Notification;
  402. Notification::LedBlinkPattern ledBlinkPattern;
  403. ledBlinkPattern.msToBeOn = paramControls.ledMsToBeOnComboBox .getSelectedItemIndex() * 200;
  404. ledBlinkPattern.msToBeOff = paramControls.ledMsToBeOffComboBox.getSelectedItemIndex() * 200;
  405. n.ledBlinkPattern = ledBlinkPattern;
  406. Array<int> vibrationPattern;
  407. if (paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() > 0 &&
  408. paramControls.vibratorMsToBeOffComboBox.getSelectedItemIndex() > 0)
  409. {
  410. vibrationPattern.add (paramControls.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500);
  411. vibrationPattern.add (paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500);
  412. vibrationPattern.add (2 * paramControls.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500);
  413. vibrationPattern.add (2 * paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500);
  414. }
  415. n.vibrationPattern = vibrationPattern;
  416. n.localOnly = paramControls.localOnlyButton.getToggleState();
  417. n.ongoing = paramControls.ongoingButton.getToggleState();
  418. n.timestampVisibility = Notification::TimestampVisibility (paramControls.timestampVisibilityComboBox.getSelectedItemIndex());
  419. if (paramControls.timeoutAfterComboBox.getSelectedItemIndex() > 0)
  420. {
  421. auto index = paramControls.timeoutAfterComboBox.getSelectedItemIndex();
  422. n.timeoutAfterMs = index * 1000 + 4000;
  423. }
  424. }
  425. void setupAccentColour()
  426. {
  427. auto accentColourSelector = std::make_unique<ColourSelector>();
  428. accentColourSelector->setName ("accent colour");
  429. accentColourSelector->setCurrentColour (paramControls.accentColourButton.findColour (TextButton::buttonColourId));
  430. accentColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
  431. accentColourSelector->setSize (200, 200);
  432. accentColourSelector->addComponentListener (this);
  433. accentColourSelector->addChangeListener (this);
  434. paramControls.accentColourSelector = accentColourSelector.get();
  435. CallOutBox::launchAsynchronously (std::move (accentColourSelector), paramControls.accentColourButton.getScreenBounds(), nullptr);
  436. }
  437. void setupLedColour()
  438. {
  439. auto ledColourSelector = std::make_unique<ColourSelector>();
  440. ledColourSelector->setName ("led colour");
  441. ledColourSelector->setCurrentColour (paramControls.ledColourButton.findColour (TextButton::buttonColourId));
  442. ledColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
  443. ledColourSelector->setSize (200, 200);
  444. ledColourSelector->addComponentListener (this);
  445. ledColourSelector->addChangeListener (this);
  446. paramControls.ledColourSelector = ledColourSelector.get();
  447. CallOutBox::launchAsynchronously (std::move (ledColourSelector), paramControls.accentColourButton.getScreenBounds(), nullptr);
  448. }
  449. void changeListenerCallback (ChangeBroadcaster* source) override
  450. {
  451. if (source == paramControls.accentColourSelector)
  452. {
  453. auto c = paramControls.accentColourSelector->getCurrentColour();
  454. paramControls.accentColourButton.setColour (TextButton::buttonColourId, c);
  455. }
  456. else if (source == paramControls.ledColourSelector)
  457. {
  458. auto c = paramControls.ledColourSelector->getCurrentColour();
  459. paramControls.ledColourButton.setColour (TextButton::buttonColourId, c);
  460. }
  461. }
  462. void componentBeingDeleted (Component& component) override
  463. {
  464. if (&component == paramControls.accentColourSelector)
  465. paramControls.accentColourSelector = nullptr;
  466. else if (&component == paramControls.ledColourSelector)
  467. paramControls.ledColourSelector = nullptr;
  468. }
  469. void handleNotification (bool isLocalNotification, const PushNotifications::Notification& n) override
  470. {
  471. ignoreUnused (isLocalNotification);
  472. auto options = MessageBoxOptions()
  473. .withIconType (MessageBoxIconType::InfoIcon)
  474. .withTitle ("Received notification")
  475. .withMessage ("ID: " + n.identifier
  476. + ", title: " + n.title
  477. + ", body: " + n.body)
  478. .withButton ("OK");
  479. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  480. }
  481. void handleNotificationAction (bool isLocalNotification,
  482. const PushNotifications::Notification& n,
  483. const juce::String& actionIdentifier,
  484. const juce::String& optionalResponse) override
  485. {
  486. ignoreUnused (isLocalNotification);
  487. auto options = MessageBoxOptions()
  488. .withIconType (MessageBoxIconType::InfoIcon)
  489. .withTitle ("Received notification action")
  490. .withMessage ("ID: " + n.identifier
  491. + ", title: " + n.title
  492. + ", body: " + n.body
  493. + ", action: " + actionIdentifier
  494. + ", optionalResponse: " + optionalResponse)
  495. .withButton ("OK");
  496. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  497. PushNotifications::getInstance()->removeDeliveredNotification (n.identifier);
  498. }
  499. void localNotificationDismissedByUser (const PushNotifications::Notification& n) override
  500. {
  501. auto options = MessageBoxOptions()
  502. .withIconType (MessageBoxIconType::InfoIcon)
  503. .withTitle ("Notification dismissed by a user")
  504. .withMessage ("ID: " + n.identifier
  505. + ", title: " + n.title
  506. + ", body: " + n.body)
  507. .withButton ("OK");
  508. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  509. }
  510. void deliveredNotificationsListReceived (const Array<PushNotifications::Notification>& notifs) override
  511. {
  512. String text = "Received notifications: ";
  513. for (auto& n : notifs)
  514. text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), ";
  515. auto options = MessageBoxOptions()
  516. .withIconType (MessageBoxIconType::InfoIcon)
  517. .withTitle ("Received notification list")
  518. .withMessage (text)
  519. .withButton ("OK");
  520. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  521. }
  522. void pendingLocalNotificationsListReceived (const Array<PushNotifications::Notification>& notifs) override
  523. {
  524. String text = "Pending notifications: ";
  525. for (auto& n : notifs)
  526. text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), ";
  527. auto options = MessageBoxOptions()
  528. .withIconType (MessageBoxIconType::InfoIcon)
  529. .withTitle ("Pending notification list")
  530. .withMessage (text)
  531. .withButton ("OK");
  532. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  533. }
  534. void deviceTokenRefreshed (const String& token) override
  535. {
  536. auto options = MessageBoxOptions()
  537. .withIconType (MessageBoxIconType::InfoIcon)
  538. .withTitle ("Device token refreshed")
  539. .withMessage (token)
  540. .withButton ("OK");
  541. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  542. }
  543. #if JUCE_ANDROID
  544. void remoteNotificationsDeleted() override
  545. {
  546. auto options = MessageBoxOptions()
  547. .withIconType (MessageBoxIconType::InfoIcon)
  548. .withTitle ("Remote notifications deleted")
  549. .withMessage ("Some of the pending messages were removed!")
  550. .withButton ("OK");
  551. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  552. }
  553. void upstreamMessageSent (const String& messageId) override
  554. {
  555. auto options = MessageBoxOptions()
  556. .withIconType (MessageBoxIconType::InfoIcon)
  557. .withTitle ("Upstream message sent")
  558. .withMessage ("Message id: " + messageId)
  559. .withButton ("OK");
  560. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  561. }
  562. void upstreamMessageSendingError (const String& messageId, const String& error) override
  563. {
  564. auto options = MessageBoxOptions()
  565. .withIconType (MessageBoxIconType::InfoIcon)
  566. .withTitle ("Upstream message sending error")
  567. .withMessage ("Message id: " + messageId
  568. + "\nerror: " + error)
  569. .withButton ("OK");
  570. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  571. }
  572. static Array<PushNotifications::Channel> getAndroidChannels()
  573. {
  574. using Channel = PushNotifications::Channel;
  575. Channel ch1, ch2, ch3;
  576. ch1.identifier = "1";
  577. ch1.name = "HighImportance";
  578. ch1.importance = PushNotifications::Channel::max;
  579. ch1.lockScreenAppearance = PushNotifications::Notification::showCompletely;
  580. ch1.description = "High Priority Channel for important stuff";
  581. ch1.groupId = "demoGroup";
  582. ch1.ledColour = Colours::red;
  583. ch1.bypassDoNotDisturb = true;
  584. ch1.canShowBadge = true;
  585. ch1.enableLights = true;
  586. ch1.enableVibration = true;
  587. ch1.soundToPlay = URL ("demonstrative");
  588. ch1.vibrationPattern = { 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200 };
  589. ch2.identifier = "2";
  590. ch2.name = "MediumImportance";
  591. ch2.importance = PushNotifications::Channel::normal;
  592. ch2.lockScreenAppearance = PushNotifications::Notification::showPartially;
  593. ch2.description = "Medium Priority Channel for standard stuff";
  594. ch2.groupId = "demoGroup";
  595. ch2.ledColour = Colours::yellow;
  596. ch2.canShowBadge = true;
  597. ch2.enableLights = true;
  598. ch2.enableVibration = true;
  599. ch2.soundToPlay = URL ("default_os_sound");
  600. ch2.vibrationPattern = { 1000, 1000 };
  601. ch3.identifier = "3";
  602. ch3.name = "LowImportance";
  603. ch3.importance = PushNotifications::Channel::min;
  604. ch3.lockScreenAppearance = PushNotifications::Notification::dontShow;
  605. ch3.description = "Low Priority Channel for silly stuff";
  606. ch3.groupId = "demoGroup";
  607. return { ch1, ch2, ch3 };
  608. }
  609. #elif JUCE_IOS || JUCE_MAC
  610. static PushNotifications::Settings getNotificationSettings()
  611. {
  612. PushNotifications::Settings settings;
  613. settings.allowAlert = true;
  614. settings.allowBadge = true;
  615. settings.allowSound = true;
  616. #if JUCE_IOS
  617. using Action = PushNotifications::Settings::Action;
  618. using Category = PushNotifications::Settings::Category;
  619. Action okAction;
  620. okAction.identifier = "okAction";
  621. okAction.title = "OK!";
  622. okAction.style = Action::button;
  623. okAction.triggerInBackground = true;
  624. Action cancelAction;
  625. cancelAction.identifier = "cancelAction";
  626. cancelAction.title = "Cancel";
  627. cancelAction.style = Action::button;
  628. cancelAction.triggerInBackground = true;
  629. cancelAction.destructive = true;
  630. Action textAction;
  631. textAction.identifier = "textAction";
  632. textAction.title = "Enter text";
  633. textAction.style = Action::text;
  634. textAction.triggerInBackground = true;
  635. textAction.destructive = false;
  636. textAction.textInputButtonText = "Ok";
  637. textAction.textInputPlaceholder = "Enter text...";
  638. Category okCategory;
  639. okCategory.identifier = "okCategory";
  640. okCategory.actions = { okAction };
  641. Category okCancelCategory;
  642. okCancelCategory.identifier = "okCancelCategory";
  643. okCancelCategory.actions = { okAction, cancelAction };
  644. Category textCategory;
  645. textCategory.identifier = "textCategory";
  646. textCategory.actions = { textAction };
  647. textCategory.sendDismissAction = true;
  648. settings.categories = { okCategory, okCancelCategory, textCategory };
  649. #endif
  650. return settings;
  651. }
  652. #endif
  653. struct RowComponent final : public Component
  654. {
  655. RowComponent (Label& l, Component& c, int u = 1)
  656. : label (l),
  657. editor (c),
  658. rowUnits (u)
  659. {
  660. addAndMakeVisible (label);
  661. addAndMakeVisible (editor);
  662. }
  663. void resized() override
  664. {
  665. auto bounds = getLocalBounds();
  666. label .setBounds (bounds.removeFromLeft (getWidth() / 3));
  667. editor.setBounds (bounds);
  668. }
  669. Label& label;
  670. Component& editor;
  671. int rowUnits;
  672. };
  673. struct ParamControls
  674. {
  675. Label identifierLabel { "identifierLabel", "Identifier" };
  676. TextEditor identifierEditor;
  677. Label titleLabel { "titleLabel", "Title" };
  678. TextEditor titleEditor;
  679. Label bodyLabel { "bodyLabel", "Body" };
  680. TextEditor bodyEditor;
  681. Label categoryLabel { "categoryLabel", "Category" };
  682. ComboBox categoryComboBox;
  683. Label channelIdLabel { "channelIdLabel", "Channel ID" };
  684. ComboBox channelIdComboBox;
  685. Label iconLabel { "iconLabel", "Icon" };
  686. ComboBox iconComboBox;
  687. Label subtitleLabel { "subtitleLabel", "Subtitle" };
  688. TextEditor subtitleEditor;
  689. Label badgeNumberLabel { "badgeNumberLabel", "BadgeNumber" };
  690. ComboBox badgeNumberComboBox;
  691. Label soundToPlayLabel { "soundToPlayLabel", "SoundToPlay" };
  692. ComboBox soundToPlayComboBox;
  693. Label propertiesLabel { "propertiesLabel", "Properties" };
  694. TextEditor propertiesEditor;
  695. Label fireInLabel { "fireInLabel", "Fire in" };
  696. ComboBox fireInComboBox;
  697. Label repeatLabel { "repeatLabel", "Repeat" };
  698. ToggleButton repeatButton;
  699. Label largeIconLabel { "largeIconLabel", "Large Icon" };
  700. ComboBox largeIconComboBox;
  701. Label badgeIconLabel { "badgeIconLabel", "Badge Icon" };
  702. ComboBox badgeIconComboBox;
  703. Label tickerTextLabel { "tickerTextLabel", "Ticker Text" };
  704. TextEditor tickerTextEditor;
  705. Label autoCancelLabel { "autoCancelLabel", "AutoCancel" };
  706. ToggleButton autoCancelButton;
  707. Label alertOnlyOnceLabel { "alertOnlyOnceLabel", "AlertOnlyOnce" };
  708. ToggleButton alertOnlyOnceButton;
  709. Label actionsLabel { "actionsLabel", "Actions" };
  710. ComboBox actionsComboBox;
  711. Label progressMaxLabel { "progressMaxLabel", "ProgressMax" };
  712. ComboBox progressMaxComboBox;
  713. Label progressCurrentLabel { "progressCurrentLabel", "ProgressCurrent" };
  714. ComboBox progressCurrentComboBox;
  715. Label progressIndeterminateLabel { "progressIndeterminateLabel", "ProgressIndeterminate" };
  716. ToggleButton progressIndeterminateButton;
  717. Label notifCategoryLabel { "notifCategoryLabel", "Category" };
  718. ComboBox notifCategoryComboBox;
  719. Label priorityLabel { "priorityLabel", "Priority" };
  720. ComboBox priorityComboBox;
  721. Label personLabel { "personLabel", "Person" };
  722. TextEditor personEditor;
  723. Label lockScreenVisibilityLabel { "lockScreenVisibilityLabel", "LockScreenVisibility" };
  724. ComboBox lockScreenVisibilityComboBox;
  725. Label groupIdLabel { "groupIdLabel", "GroupID" };
  726. TextEditor groupIdEditor;
  727. Label sortKeyLabel { "sortKeyLabel", "SortKey" };
  728. TextEditor sortKeyEditor;
  729. Label groupSummaryLabel { "groupSummaryLabel", "GroupSummary" };
  730. ToggleButton groupSummaryButton;
  731. Label groupAlertBehaviourLabel { "groupAlertBehaviourLabel", "GroupAlertBehaviour" };
  732. ComboBox groupAlertBehaviourComboBox;
  733. Label accentColourLabel { "accentColourLabel", "AccentColour" };
  734. TextButton accentColourButton;
  735. Label ledColourLabel { "ledColourLabel", "LedColour" };
  736. TextButton ledColourButton;
  737. Label ledMsToBeOnLabel { "ledMsToBeOnLabel", "LedMsToBeOn" };
  738. ComboBox ledMsToBeOnComboBox;
  739. Label ledMsToBeOffLabel { "ledMsToBeOffLabel", "LedMsToBeOff" };
  740. ComboBox ledMsToBeOffComboBox;
  741. Label vibratorMsToBeOnLabel { "vibratorMsToBeOnLabel", "VibrationMsToBeOn" };
  742. ComboBox vibratorMsToBeOnComboBox;
  743. Label vibratorMsToBeOffLabel { "vibratorMsToBeOffLabel", "VibrationMsToBeOff" };
  744. ComboBox vibratorMsToBeOffComboBox;
  745. Label localOnlyLabel { "localOnlyLabel", "LocalOnly" };
  746. ToggleButton localOnlyButton;
  747. Label ongoingLabel { "ongoingLabel", "Ongoing" };
  748. ToggleButton ongoingButton;
  749. Label timestampVisibilityLabel { "timestampVisibilityLabel", "TimestampMode" };
  750. ComboBox timestampVisibilityComboBox;
  751. Label timeoutAfterLabel { "timeoutAfterLabel", "Timeout After Ms" };
  752. ComboBox timeoutAfterComboBox;
  753. ColourSelector* accentColourSelector = nullptr;
  754. ColourSelector* ledColourSelector = nullptr;
  755. };
  756. void setupControls()
  757. {
  758. auto& pc = paramControls;
  759. StringArray categories { "okCategory", "okCancelCategory", "textCategory" };
  760. for (auto& c : categories)
  761. pc.categoryComboBox.addItem (c, pc.categoryComboBox.getNumItems() + 1);
  762. pc.categoryComboBox.setSelectedItemIndex (0);
  763. for (auto i = 1; i <= 3; ++i)
  764. pc.channelIdComboBox.addItem (String (i), i);
  765. pc.channelIdComboBox.setSelectedItemIndex (0);
  766. for (auto i = 0; i < 5; ++i)
  767. pc.iconComboBox.addItem ("icon" + String (i + 1), i + 1);
  768. pc.iconComboBox.setSelectedItemIndex (0);
  769. #if JUCE_MAC
  770. pc.iconComboBox.addItem ("none", 100);
  771. #endif
  772. pc.fireInComboBox.addItem ("Now", 1);
  773. for (auto i = 1; i < 11; ++i)
  774. pc.fireInComboBox.addItem (String (10 * i) + "seconds", i + 1);
  775. pc.fireInComboBox.setSelectedItemIndex (0);
  776. pc.largeIconComboBox.addItem ("none", 1);
  777. for (auto i = 1; i < 5; ++i)
  778. pc.largeIconComboBox.addItem ("icon" + String (i), i + 1);
  779. pc.largeIconComboBox.setSelectedItemIndex (0);
  780. pc.badgeIconComboBox.addItem ("none", 1);
  781. pc.badgeIconComboBox.addItem ("small", 2);
  782. pc.badgeIconComboBox.addItem ("large", 3);
  783. pc.badgeIconComboBox.setSelectedItemIndex (2);
  784. pc.actionsComboBox.addItem ("none", 1);
  785. pc.actionsComboBox.addItem ("ok-cancel", 2);
  786. pc.actionsComboBox.addItem ("text-input", 3);
  787. #if JUCE_ANDROID
  788. pc.actionsComboBox.addItem ("ok-cancel-icons", 4);
  789. pc.actionsComboBox.addItem ("text-input-limited_responses", 5);
  790. #endif
  791. pc.actionsComboBox.setSelectedItemIndex (0);
  792. for (auto i = 0; i < 7; ++i)
  793. pc.badgeNumberComboBox.addItem (String (i), i + 1);
  794. pc.badgeNumberComboBox.setSelectedItemIndex (0);
  795. #if JUCE_IOS
  796. String prefix = "Notifications/sounds/";
  797. String extension = ".caf";
  798. #else
  799. String prefix;
  800. String extension;
  801. #endif
  802. pc.soundToPlayComboBox.addItem ("none", 1);
  803. pc.soundToPlayComboBox.addItem ("default_os_sound", 2);
  804. pc.soundToPlayComboBox.addItem (prefix + "demonstrative" + extension, 3);
  805. pc.soundToPlayComboBox.addItem (prefix + "isntit" + extension, 4);
  806. pc.soundToPlayComboBox.addItem (prefix + "jinglebellssms" + extension, 5);
  807. pc.soundToPlayComboBox.addItem (prefix + "served" + extension, 6);
  808. pc.soundToPlayComboBox.addItem (prefix + "solemn" + extension, 7);
  809. pc.soundToPlayComboBox.setSelectedItemIndex (1);
  810. for (auto i = 0; i < 11; ++i)
  811. {
  812. pc.progressMaxComboBox .addItem (String (i * 10) + "%", i + 1);
  813. pc.progressCurrentComboBox.addItem (String (i * 10) + "%", i + 1);
  814. }
  815. pc.progressMaxComboBox .setSelectedItemIndex (0);
  816. pc.progressCurrentComboBox.setSelectedItemIndex (0);
  817. pc.notifCategoryComboBox.addItem ("unspecified", 1);
  818. pc.notifCategoryComboBox.addItem ("alarm", 2);
  819. pc.notifCategoryComboBox.addItem ("call", 3);
  820. pc.notifCategoryComboBox.addItem ("email", 4);
  821. pc.notifCategoryComboBox.addItem ("error", 5);
  822. pc.notifCategoryComboBox.addItem ("event", 6);
  823. pc.notifCategoryComboBox.addItem ("message", 7);
  824. pc.notifCategoryComboBox.addItem ("progress", 8);
  825. pc.notifCategoryComboBox.addItem ("promo", 9);
  826. pc.notifCategoryComboBox.addItem ("recommendation", 10);
  827. pc.notifCategoryComboBox.addItem ("reminder", 11);
  828. pc.notifCategoryComboBox.addItem ("service", 12);
  829. pc.notifCategoryComboBox.addItem ("social", 13);
  830. pc.notifCategoryComboBox.addItem ("status", 14);
  831. pc.notifCategoryComboBox.addItem ("system", 15);
  832. pc.notifCategoryComboBox.addItem ("transport", 16);
  833. pc.notifCategoryComboBox.setSelectedItemIndex (0);
  834. for (auto i = -2; i < 3; ++i)
  835. pc.priorityComboBox.addItem (String (i), i + 3);
  836. pc.priorityComboBox.setSelectedItemIndex (2);
  837. pc.lockScreenVisibilityComboBox.addItem ("don't show", 1);
  838. pc.lockScreenVisibilityComboBox.addItem ("show partially", 2);
  839. pc.lockScreenVisibilityComboBox.addItem ("show completely", 3);
  840. pc.lockScreenVisibilityComboBox.setSelectedItemIndex (1);
  841. pc.groupAlertBehaviourComboBox.addItem ("alert all", 1);
  842. pc.groupAlertBehaviourComboBox.addItem ("alert summary", 2);
  843. pc.groupAlertBehaviourComboBox.addItem ("alert children", 3);
  844. pc.groupAlertBehaviourComboBox.setSelectedItemIndex (0);
  845. pc.timeoutAfterComboBox.addItem ("No timeout", 1);
  846. for (auto i = 0; i < 10; ++i)
  847. {
  848. pc.ledMsToBeOnComboBox .addItem (String (i * 200) + "ms", i + 1);
  849. pc.ledMsToBeOffComboBox .addItem (String (i * 200) + "ms", i + 1);
  850. pc.vibratorMsToBeOnComboBox .addItem (String (i * 500) + "ms", i + 1);
  851. pc.vibratorMsToBeOffComboBox.addItem (String (i * 500) + "ms", i + 1);
  852. pc.timeoutAfterComboBox .addItem (String (5000 + 1000 * i) + "ms", i + 2);
  853. }
  854. pc.ledMsToBeOnComboBox .setSelectedItemIndex (5);
  855. pc.ledMsToBeOffComboBox .setSelectedItemIndex (5);
  856. pc.vibratorMsToBeOnComboBox .setSelectedItemIndex (0);
  857. pc.vibratorMsToBeOffComboBox.setSelectedItemIndex (0);
  858. pc.timeoutAfterComboBox .setSelectedItemIndex (0);
  859. pc.timestampVisibilityComboBox.addItem ("off", 1);
  860. pc.timestampVisibilityComboBox.addItem ("on", 2);
  861. pc.timestampVisibilityComboBox.addItem ("chronometer", 3);
  862. pc.timestampVisibilityComboBox.addItem ("count down", 4);
  863. pc.timestampVisibilityComboBox.setSelectedItemIndex (1);
  864. }
  865. void distributeControls()
  866. {
  867. auto& pc = paramControls;
  868. paramsOneView .addRowComponent (new RowComponent (pc.identifierLabel, pc.identifierEditor));
  869. paramsOneView .addRowComponent (new RowComponent (pc.titleLabel, pc.titleEditor));
  870. paramsOneView .addRowComponent (new RowComponent (pc.bodyLabel, pc.bodyEditor, 4));
  871. #if JUCE_IOS
  872. paramsOneView .addRowComponent (new RowComponent (pc.categoryLabel, pc.categoryComboBox));
  873. #elif JUCE_ANDROID
  874. paramsOneView .addRowComponent (new RowComponent (pc.channelIdLabel, pc.channelIdComboBox));
  875. #endif
  876. #if JUCE_ANDROID || JUCE_MAC
  877. paramsOneView .addRowComponent (new RowComponent (pc.iconLabel, pc.iconComboBox));
  878. #endif
  879. paramsTwoView .addRowComponent (new RowComponent (pc.subtitleLabel, pc.subtitleEditor));
  880. #if ! JUCE_MAC
  881. paramsTwoView .addRowComponent (new RowComponent (pc.badgeNumberLabel, pc.badgeNumberComboBox));
  882. #endif
  883. paramsTwoView .addRowComponent (new RowComponent (pc.soundToPlayLabel, pc.soundToPlayComboBox));
  884. paramsTwoView .addRowComponent (new RowComponent (pc.propertiesLabel, pc.propertiesEditor, 3));
  885. #if JUCE_IOS || JUCE_MAC
  886. paramsTwoView .addRowComponent (new RowComponent (pc.fireInLabel, pc.fireInComboBox));
  887. paramsTwoView .addRowComponent (new RowComponent (pc.repeatLabel, pc.repeatButton));
  888. #elif JUCE_ANDROID
  889. paramsTwoView .addRowComponent (new RowComponent (pc.largeIconLabel, pc.largeIconComboBox));
  890. paramsTwoView .addRowComponent (new RowComponent (pc.badgeIconLabel, pc.badgeIconComboBox));
  891. paramsTwoView .addRowComponent (new RowComponent (pc.tickerTextLabel, pc.tickerTextEditor));
  892. paramsTwoView .addRowComponent (new RowComponent (pc.autoCancelLabel, pc.autoCancelButton));
  893. paramsTwoView .addRowComponent (new RowComponent (pc.alertOnlyOnceLabel, pc.alertOnlyOnceButton));
  894. #endif
  895. #if JUCE_ANDROID || JUCE_MAC
  896. paramsTwoView .addRowComponent (new RowComponent (pc.actionsLabel, pc.actionsComboBox));
  897. #endif
  898. #if JUCE_ANDROID
  899. paramsThreeView.addRowComponent (new RowComponent (pc.progressMaxLabel, pc.progressMaxComboBox));
  900. paramsThreeView.addRowComponent (new RowComponent (pc.progressCurrentLabel, pc.progressCurrentComboBox));
  901. paramsThreeView.addRowComponent (new RowComponent (pc.progressIndeterminateLabel, pc.progressIndeterminateButton));
  902. paramsThreeView.addRowComponent (new RowComponent (pc.categoryLabel, pc.categoryComboBox));
  903. paramsThreeView.addRowComponent (new RowComponent (pc.priorityLabel, pc.priorityComboBox));
  904. paramsThreeView.addRowComponent (new RowComponent (pc.personLabel, pc.personEditor));
  905. paramsThreeView.addRowComponent (new RowComponent (pc.lockScreenVisibilityLabel, pc.lockScreenVisibilityComboBox));
  906. paramsThreeView.addRowComponent (new RowComponent (pc.groupIdLabel, pc.groupIdEditor));
  907. paramsThreeView.addRowComponent (new RowComponent (pc.sortKeyLabel, pc.sortKeyEditor));
  908. paramsThreeView.addRowComponent (new RowComponent (pc.groupSummaryLabel, pc.groupSummaryButton));
  909. paramsThreeView.addRowComponent (new RowComponent (pc.groupAlertBehaviourLabel, pc.groupAlertBehaviourComboBox));
  910. paramsFourView .addRowComponent (new RowComponent (pc.accentColourLabel, pc.accentColourButton));
  911. paramsFourView .addRowComponent (new RowComponent (pc.ledColourLabel, pc.ledColourButton));
  912. paramsFourView .addRowComponent (new RowComponent (pc.ledMsToBeOffLabel, pc.ledMsToBeOffComboBox));
  913. paramsFourView .addRowComponent (new RowComponent (pc.ledMsToBeOnLabel, pc.ledMsToBeOnComboBox));
  914. paramsFourView .addRowComponent (new RowComponent (pc.vibratorMsToBeOffLabel, pc.vibratorMsToBeOffComboBox));
  915. paramsFourView .addRowComponent (new RowComponent (pc.vibratorMsToBeOnLabel, pc.vibratorMsToBeOnComboBox));
  916. paramsFourView .addRowComponent (new RowComponent (pc.localOnlyLabel, pc.localOnlyButton));
  917. paramsFourView .addRowComponent (new RowComponent (pc.ongoingLabel, pc.ongoingButton));
  918. paramsFourView .addRowComponent (new RowComponent (pc.timestampVisibilityLabel, pc.timestampVisibilityComboBox));
  919. paramsFourView .addRowComponent (new RowComponent (pc.timeoutAfterLabel, pc.timeoutAfterComboBox));
  920. #endif
  921. }
  922. struct ParamsView final : public Component
  923. {
  924. ParamsView()
  925. {
  926. // For now, to be able to dismiss mobile keyboard.
  927. setWantsKeyboardFocus (true);
  928. }
  929. void addRowComponent (RowComponent* rc)
  930. {
  931. rowComponents.add (rc);
  932. addAndMakeVisible (rc);
  933. }
  934. void resized() override
  935. {
  936. auto totalRowUnits = 0;
  937. for (auto* rc : rowComponents)
  938. totalRowUnits += rc->rowUnits;
  939. auto rowHeight = getHeight() / totalRowUnits;
  940. auto bounds = getLocalBounds();
  941. for (auto* rc : rowComponents)
  942. rc->setBounds (bounds.removeFromTop (rc->rowUnits * rowHeight));
  943. auto* last = rowComponents[rowComponents.size() - 1];
  944. last->setBounds (last->getBounds().withHeight (getHeight() - last->getY()));
  945. }
  946. private:
  947. OwnedArray<RowComponent> rowComponents;
  948. };
  949. struct AuxActionsView final : public Component
  950. {
  951. AuxActionsView()
  952. {
  953. addAndMakeVisible (getDeliveredNotificationsButton);
  954. addAndMakeVisible (removeDeliveredNotifWithIdButton);
  955. addAndMakeVisible (deliveredNotifIdentifier);
  956. addAndMakeVisible (removeAllDeliveredNotifsButton);
  957. #if JUCE_IOS || JUCE_MAC
  958. addAndMakeVisible (getPendingNotificationsButton);
  959. addAndMakeVisible (removePendingNotifWithIdButton);
  960. addAndMakeVisible (pendingNotifIdentifier);
  961. addAndMakeVisible (removeAllPendingNotifsButton);
  962. #endif
  963. // For now, to be able to dismiss mobile keyboard.
  964. setWantsKeyboardFocus (true);
  965. }
  966. void resized() override
  967. {
  968. auto columnWidth = getWidth();
  969. auto rowHeight = getHeight() / 6;
  970. auto bounds = getLocalBounds();
  971. getDeliveredNotificationsButton .setBounds (bounds.removeFromTop (rowHeight));
  972. auto rowBounds = bounds.removeFromTop (rowHeight);
  973. removeDeliveredNotifWithIdButton.setBounds (rowBounds.removeFromLeft (columnWidth / 2));
  974. deliveredNotifIdentifier .setBounds (rowBounds);
  975. removeAllDeliveredNotifsButton .setBounds (bounds.removeFromTop (rowHeight));
  976. #if JUCE_IOS || JUCE_MAC
  977. getPendingNotificationsButton .setBounds (bounds.removeFromTop (rowHeight));
  978. rowBounds = bounds.removeFromTop (rowHeight);
  979. removePendingNotifWithIdButton.setBounds (rowBounds.removeFromLeft (columnWidth / 2));
  980. pendingNotifIdentifier .setBounds (rowBounds);
  981. removeAllPendingNotifsButton .setBounds (bounds.removeFromTop (rowHeight));
  982. #endif
  983. }
  984. TextButton getDeliveredNotificationsButton { "Get Delivered Notifications" };
  985. TextButton removeDeliveredNotifWithIdButton { "Remove Delivered Notif With ID:" };
  986. TextEditor deliveredNotifIdentifier;
  987. TextButton removeAllDeliveredNotifsButton { "Remove All Delivered Notifs" };
  988. TextButton getPendingNotificationsButton { "Get Pending Notifications" };
  989. TextButton removePendingNotifWithIdButton { "Remove Pending Notif With ID:" };
  990. TextEditor pendingNotifIdentifier;
  991. TextButton removeAllPendingNotifsButton { "Remove All Pending Notifs" };
  992. };
  993. struct RemoteView final : public Component
  994. {
  995. RemoteView()
  996. {
  997. addAndMakeVisible (getDeviceTokenButton);
  998. #if JUCE_ANDROID
  999. addAndMakeVisible (sendRemoteMessageButton);
  1000. addAndMakeVisible (subscribeToSportsButton);
  1001. addAndMakeVisible (unsubscribeFromSportsButton);
  1002. #endif
  1003. }
  1004. void resized()
  1005. {
  1006. auto rowSize = getHeight() / 10;
  1007. auto bounds = getLocalBounds().reduced (getWidth() / 10, getHeight() / 10);
  1008. bounds.removeFromTop (2 * rowSize);
  1009. getDeviceTokenButton .setBounds (bounds.removeFromTop (rowSize));
  1010. sendRemoteMessageButton .setBounds (bounds.removeFromTop (rowSize));
  1011. subscribeToSportsButton .setBounds (bounds.removeFromTop (rowSize));
  1012. unsubscribeFromSportsButton.setBounds (bounds.removeFromTop (rowSize));
  1013. }
  1014. TextButton getDeviceTokenButton { "GetDeviceToken" };
  1015. TextButton sendRemoteMessageButton { "SendRemoteMessage" };
  1016. TextButton subscribeToSportsButton { "SubscribeToSports" };
  1017. TextButton unsubscribeFromSportsButton { "UnsubscribeFromSports" };
  1018. };
  1019. struct DemoTabbedComponent final : public TabbedComponent
  1020. {
  1021. DemoTabbedComponent (PushNotificationsDemo& demoIn, TabbedButtonBar::Orientation orientation)
  1022. : TabbedComponent (orientation), demo (demoIn)
  1023. {
  1024. }
  1025. void currentTabChanged (int, const String& newCurrentTabName) override
  1026. {
  1027. if (! showedRemoteInstructions && newCurrentTabName == "Remote")
  1028. {
  1029. demo.showRemoteInstructions();
  1030. showedRemoteInstructions = true;
  1031. }
  1032. }
  1033. private:
  1034. bool showedRemoteInstructions = false;
  1035. PushNotificationsDemo& demo;
  1036. };
  1037. void showRemoteInstructions()
  1038. {
  1039. #if JUCE_IOS || JUCE_MAC
  1040. auto options = MessageBoxOptions()
  1041. .withIconType (MessageBoxIconType::InfoIcon)
  1042. .withTitle ("Remote Notifications instructions")
  1043. .withMessage ("In order to be able to test remote notifications "
  1044. "ensure that the app is signed and that you register "
  1045. "the bundle ID for remote notifications in "
  1046. "Apple Developer Center.")
  1047. .withButton ("OK");
  1048. messageBox = NativeMessageBox::showScopedAsync (options, nullptr);
  1049. #endif
  1050. }
  1051. Label headerLabel { "headerLabel", "Push Notifications Demo" };
  1052. ParamControls paramControls;
  1053. ParamsView paramsOneView, paramsTwoView, paramsThreeView, paramsFourView;
  1054. AuxActionsView auxActionsView;
  1055. TabbedComponent localNotificationsTabs { TabbedButtonBar::TabsAtTop };
  1056. RemoteView remoteView;
  1057. DemoTabbedComponent mainTabs { *this, TabbedButtonBar::TabsAtTop };
  1058. TextButton sendButton { "Send!" };
  1059. Label notAvailableYetLabel { "notAvailableYetLabel",
  1060. "Push Notifications feature is not available on this platform yet!" };
  1061. ScopedMessageBox messageBox;
  1062. //==============================================================================
  1063. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PushNotificationsDemo)
  1064. };