| @@ -31,19 +31,25 @@ | |||
| </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="BinaryResources/sounds/demonstrative.caf" xcodeResource="0"/> | |||
| <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="BinaryResources/sounds/demonstrative.mp3" xcodeResource="1"/> | |||
| <FILE id="PWlDpi" name="isntit.caf" compile="0" resource="1" file="BinaryResources/sounds/isntit.caf" | |||
| xcodeResource="0"/> | |||
| <FILE id="nbNoVQ" name="isntit.mp3" compile="0" resource="1" file="BinaryResources/sounds/isntit.mp3" | |||
| xcodeResource="1"/> | |||
| <FILE id="AheEYO" name="jinglebellssms.caf" compile="0" resource="1" | |||
| file="BinaryResources/sounds/jinglebellssms.caf"/> | |||
| file="BinaryResources/sounds/jinglebellssms.caf" xcodeResource="0"/> | |||
| <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"/> | |||
| file="BinaryResources/sounds/jinglebellssms.mp3" xcodeResource="1"/> | |||
| <FILE id="TmLrYM" name="served.caf" compile="0" resource="1" file="BinaryResources/sounds/served.caf" | |||
| xcodeResource="0"/> | |||
| <FILE id="Ad45fH" name="served.mp3" compile="0" resource="1" file="BinaryResources/sounds/served.mp3" | |||
| xcodeResource="1"/> | |||
| <FILE id="qH4XUR" name="solemn.caf" compile="0" resource="1" file="BinaryResources/sounds/solemn.caf" | |||
| xcodeResource="0"/> | |||
| <FILE id="cwrEXS" name="solemn.mp3" compile="0" resource="1" file="BinaryResources/sounds/solemn.mp3" | |||
| xcodeResource="1"/> | |||
| </GROUP> | |||
| </GROUP> | |||
| <GROUP id="{1AF8C966-743E-3F06-189C-AADD83767CB4}" name="Source"> | |||
| @@ -104,7 +110,7 @@ | |||
| <MODULEPATH id="juce_audio_processors" path="../../modules"/> | |||
| </MODULEPATHS> | |||
| </ANDROIDSTUDIO> | |||
| <XCODE_MAC targetFolder="Builds/MacOSX"> | |||
| <XCODE_MAC targetFolder="Builds/MacOSX" iosPushNotifications="1" customXcodeResourceFolders="./BinaryResources/images"> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="PushNotificationsDemo" | |||
| enablePluginBinaryCopyStep="1"/> | |||
| @@ -29,7 +29,10 @@ | |||
| //============================================================================== | |||
| MainContentComponent::MainContentComponent() | |||
| { | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| setupControls(); | |||
| distributeControls(); | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| addAndMakeVisible (headerLabel); | |||
| addAndMakeVisible (mainTabs); | |||
| addAndMakeVisible (sendButton); | |||
| @@ -40,12 +43,18 @@ MainContentComponent::MainContentComponent() | |||
| headerLabel.setJustificationType (Justification::centred); | |||
| notAvailableYetLabel.setJustificationType (Justification::centred); | |||
| #if JUCE_MAC | |||
| StringArray tabNames { "Params1", "Params2", "Params3", "Params4" }; | |||
| #else | |||
| StringArray tabNames { "Req. params", "Opt. params1", "Opt. params2", "Opt. params3" }; | |||
| #endif | |||
| const auto colour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId); | |||
| localNotificationsTabs.addTab ("Req. params", colour, &requiredParamsView, false); | |||
| localNotificationsTabs.addTab ("Opt. params1", colour, &optionalParamsOneView, false); | |||
| localNotificationsTabs.addTab (tabNames[0], colour, ¶msOneView, false); | |||
| localNotificationsTabs.addTab (tabNames[1], colour, ¶msTwoView, false); | |||
| #if JUCE_ANDROID | |||
| localNotificationsTabs.addTab ("Opt. params2", colour, &optionalParamsTwoView, false); | |||
| localNotificationsTabs.addTab ("Opt. params3", colour, &optionalParamsThreeView, false); | |||
| localNotificationsTabs.addTab (tabNames[2], colour, ¶msThreeView, false); | |||
| localNotificationsTabs.addTab (tabNames[3], colour, ¶msFourView, false); | |||
| #endif | |||
| localNotificationsTabs.addTab ("Aux. actions", colour, &auxActionsView, false); | |||
| @@ -63,7 +72,7 @@ MainContentComponent::MainContentComponent() | |||
| auxActionsView.getDeliveredNotificationsButton .addListener (this); | |||
| auxActionsView.removeDeliveredNotifWithIdButton.addListener (this); | |||
| auxActionsView.removeAllDeliveredNotifsButton .addListener (this); | |||
| #if JUCE_IOS | |||
| #if JUCE_IOS || JUCE_MAC | |||
| auxActionsView.getPendingNotificationsButton .addListener (this); | |||
| auxActionsView.removePendingNotifWithIdButton.addListener (this); | |||
| auxActionsView.removeAllPendingNotifsButton .addListener (this); | |||
| @@ -74,16 +83,16 @@ MainContentComponent::MainContentComponent() | |||
| remoteView.subscribeToSportsButton .addListener (this); | |||
| remoteView.unsubscribeFromSportsButton.addListener (this); | |||
| optionalParamsThreeView.accentColourButton.addListener (this); | |||
| optionalParamsThreeView.ledColourButton .addListener (this); | |||
| paramControls.accentColourButton.addListener (this); | |||
| paramControls.ledColourButton .addListener (this); | |||
| jassert (PushNotifications::getInstance()->areNotificationsEnabled()); | |||
| PushNotifications::getInstance()->addListener (this); | |||
| #if JUCE_IOS | |||
| optionalParamsOneView.fireInComboBox.addListener (this); | |||
| PushNotifications::getInstance()->requestPermissionsWithSettings (getIosSettings()); | |||
| #if JUCE_IOS || JUCE_MAC | |||
| paramControls.fireInComboBox.addListener (this); | |||
| PushNotifications::getInstance()->requestPermissionsWithSettings (getNotificationSettings()); | |||
| #elif JUCE_ANDROID | |||
| PushNotifications::ChannelGroup cg { "demoGroup", "demo group" }; | |||
| PushNotifications::getInstance()->setupChannels ({{ cg }}, getAndroidChannels()); | |||
| @@ -95,6 +104,200 @@ MainContentComponent::~MainContentComponent() | |||
| PushNotifications::getInstance()->removeListener (this); | |||
| } | |||
| void MainContentComponent::setupControls() | |||
| { | |||
| auto& pc = paramControls; | |||
| StringArray categories { "okCategory", "okCancelCategory", "textCategory" }; | |||
| for (const auto& c : categories) | |||
| pc.categoryComboBox.addItem (c, pc.categoryComboBox.getNumItems() + 1); | |||
| pc.categoryComboBox.setSelectedItemIndex (0); | |||
| for (int i = 1; i <= 3; ++i) | |||
| pc.channelIdComboBox.addItem (String (i), i); | |||
| pc.channelIdComboBox.setSelectedItemIndex (0); | |||
| for (int i = 0; i < 5; ++i) | |||
| pc.iconComboBox.addItem ("icon" + String (i + 1), i + 1); | |||
| pc.iconComboBox.setSelectedItemIndex (0); | |||
| #if JUCE_MAC | |||
| pc.iconComboBox.addItem ("none", 100); | |||
| #endif | |||
| pc.fireInComboBox.addItem ("Now", 1); | |||
| for (int i = 1; i < 11; ++i) | |||
| pc.fireInComboBox.addItem (String (10 * i) + "seconds", i + 1); | |||
| pc.fireInComboBox.setSelectedItemIndex (0); | |||
| pc.largeIconComboBox.addItem ("none", 1); | |||
| for (int i = 1; i < 5; ++i) | |||
| pc.largeIconComboBox.addItem ("icon" + String (i), i + 1); | |||
| pc.largeIconComboBox.setSelectedItemIndex (0); | |||
| pc.badgeIconComboBox.addItem ("none", 1); | |||
| pc.badgeIconComboBox.addItem ("small", 2); | |||
| pc.badgeIconComboBox.addItem ("large", 3); | |||
| pc.badgeIconComboBox.setSelectedItemIndex (2); | |||
| pc.actionsComboBox.addItem ("none", 1); | |||
| pc.actionsComboBox.addItem ("ok-cancel", 2); | |||
| pc.actionsComboBox.addItem ("text-input", 3); | |||
| #if JUCE_ANDROID | |||
| pc.actionsComboBox.addItem ("ok-cancel-icons", 4); | |||
| pc.actionsComboBox.addItem ("text-input-limited_responses", 5); | |||
| #endif | |||
| pc.actionsComboBox.setSelectedItemIndex (0); | |||
| for (int i = 0; i < 7; ++i) | |||
| pc.badgeNumberComboBox.addItem (String (i), i + 1); | |||
| pc.badgeNumberComboBox.setSelectedItemIndex (0); | |||
| #if JUCE_IOS | |||
| String prefix = "sounds/"; | |||
| String extension = ".caf"; | |||
| #else | |||
| String prefix; | |||
| String extension; | |||
| #endif | |||
| pc.soundToPlayComboBox.addItem ("none", 1); | |||
| pc.soundToPlayComboBox.addItem ("default_os_sound", 2); | |||
| pc.soundToPlayComboBox.addItem (prefix + "demonstrative" + extension, 3); | |||
| pc.soundToPlayComboBox.addItem (prefix + "isntit" + extension, 4); | |||
| pc.soundToPlayComboBox.addItem (prefix + "jinglebellssms" + extension, 5); | |||
| pc.soundToPlayComboBox.addItem (prefix + "served" + extension, 6); | |||
| pc.soundToPlayComboBox.addItem (prefix + "solemn" + extension, 7); | |||
| pc.soundToPlayComboBox.setSelectedItemIndex (1); | |||
| for (int i = 0; i < 11; ++i) | |||
| { | |||
| pc.progressMaxComboBox .addItem (String (i * 10) + "%", i + 1); | |||
| pc.progressCurrentComboBox.addItem (String (i * 10) + "%", i + 1); | |||
| } | |||
| pc.progressMaxComboBox .setSelectedItemIndex (0); | |||
| pc.progressCurrentComboBox.setSelectedItemIndex (0); | |||
| pc.notifCategoryComboBox.addItem ("unspecified", 1); | |||
| pc.notifCategoryComboBox.addItem ("alarm", 2); | |||
| pc.notifCategoryComboBox.addItem ("call", 3); | |||
| pc.notifCategoryComboBox.addItem ("email", 4); | |||
| pc.notifCategoryComboBox.addItem ("error", 5); | |||
| pc.notifCategoryComboBox.addItem ("event", 6); | |||
| pc.notifCategoryComboBox.addItem ("message", 7); | |||
| pc.notifCategoryComboBox.addItem ("progress", 8); | |||
| pc.notifCategoryComboBox.addItem ("promo", 9); | |||
| pc.notifCategoryComboBox.addItem ("recommendation", 10); | |||
| pc.notifCategoryComboBox.addItem ("reminder", 11); | |||
| pc.notifCategoryComboBox.addItem ("service", 12); | |||
| pc.notifCategoryComboBox.addItem ("social", 13); | |||
| pc.notifCategoryComboBox.addItem ("status", 14); | |||
| pc.notifCategoryComboBox.addItem ("system", 15); | |||
| pc.notifCategoryComboBox.addItem ("transport", 16); | |||
| pc.notifCategoryComboBox.setSelectedItemIndex (0); | |||
| for (int i = -2; i < 3; ++i) | |||
| pc.priorityComboBox.addItem (String (i), i + 3); | |||
| pc.priorityComboBox.setSelectedItemIndex (2); | |||
| pc.lockScreenVisibilityComboBox.addItem ("don't show", 1); | |||
| pc.lockScreenVisibilityComboBox.addItem ("show partially", 2); | |||
| pc.lockScreenVisibilityComboBox.addItem ("show completely", 3); | |||
| pc.lockScreenVisibilityComboBox.setSelectedItemIndex (1); | |||
| pc.groupAlertBehaviourComboBox.addItem ("alert all", 1); | |||
| pc.groupAlertBehaviourComboBox.addItem ("alert summary", 2); | |||
| pc.groupAlertBehaviourComboBox.addItem ("alert children", 3); | |||
| pc.groupAlertBehaviourComboBox.setSelectedItemIndex (0); | |||
| pc.timeoutAfterComboBox.addItem ("No timeout", 1); | |||
| for (int i = 0; i < 10; ++i) | |||
| { | |||
| pc.ledMsToBeOnComboBox .addItem (String (i * 200) + "ms", i + 1); | |||
| pc.ledMsToBeOffComboBox .addItem (String (i * 200) + "ms", i + 1); | |||
| pc.vibratorMsToBeOnComboBox .addItem (String (i * 500) + "ms", i + 1); | |||
| pc.vibratorMsToBeOffComboBox.addItem (String (i * 500) + "ms", i + 1); | |||
| pc.timeoutAfterComboBox.addItem (String (5000 + 1000 * i) + "ms", i + 2); | |||
| } | |||
| pc.ledMsToBeOnComboBox .setSelectedItemIndex (5); | |||
| pc.ledMsToBeOffComboBox .setSelectedItemIndex (5); | |||
| pc.vibratorMsToBeOnComboBox .setSelectedItemIndex (0); | |||
| pc.vibratorMsToBeOffComboBox.setSelectedItemIndex (0); | |||
| pc.timeoutAfterComboBox.setSelectedItemIndex (0); | |||
| pc.timestampVisibilityComboBox.addItem ("off", 1); | |||
| pc.timestampVisibilityComboBox.addItem ("on", 2); | |||
| pc.timestampVisibilityComboBox.addItem ("chronometer", 3); | |||
| pc.timestampVisibilityComboBox.addItem ("count down", 4); | |||
| pc.timestampVisibilityComboBox.setSelectedItemIndex (1); | |||
| } | |||
| void MainContentComponent::distributeControls() | |||
| { | |||
| auto& pc = paramControls; | |||
| paramsOneView.addRowComponent (new RowComponent (pc.identifierLabel, pc.identifierEditor)); | |||
| paramsOneView.addRowComponent (new RowComponent (pc.titleLabel, pc.titleEditor)); | |||
| paramsOneView.addRowComponent (new RowComponent (pc.bodyLabel, pc.bodyEditor, 4)); | |||
| #if JUCE_IOS | |||
| paramsOneView.addRowComponent (new RowComponent (pc.categoryLabel, pc.categoryComboBox)); | |||
| #elif JUCE_ANDROID | |||
| paramsOneView.addRowComponent (new RowComponent (pc.channelIdLabel, pc.channelIdComboBox)); | |||
| #endif | |||
| #if JUCE_ANDROID || JUCE_MAC | |||
| paramsOneView.addRowComponent (new RowComponent (pc.iconLabel, pc.iconComboBox)); | |||
| #endif | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.subtitleLabel, pc.subtitleEditor)); | |||
| #if ! JUCE_MAC | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.badgeNumberLabel, pc.badgeNumberComboBox)); | |||
| #endif | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.soundToPlayLabel, pc.soundToPlayComboBox)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.propertiesLabel, pc.propertiesEditor, 3)); | |||
| #if JUCE_IOS || JUCE_MAC | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.fireInLabel, pc.fireInComboBox)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.repeatLabel, pc.repeatButton)); | |||
| #elif JUCE_ANDROID | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.largeIconLabel, pc.largeIconComboBox)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.badgeIconLabel, pc.badgeIconComboBox)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.tickerTextLabel, pc.tickerTextEditor)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.autoCancelLabel, pc.autoCancelButton)); | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.alertOnlyOnceLabel, pc.alertOnlyOnceButton)); | |||
| #endif | |||
| #if JUCE_ANDROID || JUCE_MAC | |||
| paramsTwoView.addRowComponent (new RowComponent (pc.actionsLabel, pc.actionsComboBox)); | |||
| #endif | |||
| #if JUCE_ANDROID | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.progressMaxLabel, pc.progressMaxComboBox)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.progressCurrentLabel, pc.progressCurrentComboBox)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.progressIndeterminateLabel, pc.progressIndeterminateButton)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.categoryLabel, pc.categoryComboBox)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.priorityLabel, pc.priorityComboBox)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.personLabel, pc.personEditor)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.lockScreenVisibilityLabel, pc.lockScreenVisibilityComboBox)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.groupIdLabel, pc.groupIdEditor)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.sortKeyLabel, pc.sortKeyEditor)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.groupSummaryLabel, pc.groupSummaryButton)); | |||
| paramsThreeView.addRowComponent (new RowComponent (pc.groupAlertBehaviourLabel, pc.groupAlertBehaviourComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.accentColourLabel, pc.accentColourButton)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.ledColourLabel, pc.ledColourButton)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.ledMsToBeOffLabel, pc.ledMsToBeOffComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.ledMsToBeOnLabel, pc.ledMsToBeOnComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.vibratorMsToBeOffLabel, pc.vibratorMsToBeOffComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.vibratorMsToBeOnLabel, pc.vibratorMsToBeOnComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.localOnlyLabel, pc.localOnlyButton)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.ongoingLabel, pc.ongoingButton)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.timestampVisibilityLabel, pc.timestampVisibilityComboBox)); | |||
| paramsFourView.addRowComponent (new RowComponent (pc.timeoutAfterLabel, pc.timeoutAfterComboBox)); | |||
| #endif | |||
| } | |||
| void MainContentComponent::paint (Graphics& g) | |||
| { | |||
| g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); | |||
| @@ -117,9 +320,9 @@ void MainContentComponent::buttonClicked (Button* b) | |||
| { | |||
| if (b == &sendButton) | |||
| sendLocalNotification(); | |||
| else if (b == &optionalParamsThreeView.accentColourButton) | |||
| else if (b == ¶mControls.accentColourButton) | |||
| setupAccentColour(); | |||
| else if (b == &optionalParamsThreeView.ledColourButton) | |||
| else if (b == ¶mControls.ledColourButton) | |||
| setupLedColour(); | |||
| else if (b == &auxActionsView.getDeliveredNotificationsButton) | |||
| getDeliveredNotifications(); | |||
| @@ -127,7 +330,7 @@ void MainContentComponent::buttonClicked (Button* b) | |||
| PushNotifications::getInstance()->removeDeliveredNotification (auxActionsView.deliveredNotifIdentifier.getText()); | |||
| else if (b == &auxActionsView.removeAllDeliveredNotifsButton) | |||
| PushNotifications::getInstance()->removeAllDeliveredNotifications(); | |||
| #if JUCE_IOS | |||
| #if JUCE_IOS || JUCE_MAC | |||
| else if (b == &auxActionsView.getPendingNotificationsButton) | |||
| PushNotifications::getInstance()->getPendingLocalNotifications(); | |||
| else if (b == &auxActionsView.removePendingNotifWithIdButton) | |||
| @@ -141,7 +344,10 @@ void MainContentComponent::buttonClicked (Button* b) | |||
| DBG ("token = " + token); | |||
| NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Device token", token); | |||
| if (token.isEmpty()) | |||
| showRemoteInstructions(); | |||
| else | |||
| NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Device token", token); | |||
| } | |||
| #if JUCE_ANDROID | |||
| else if (b == &remoteView.sendRemoteMessageButton) | |||
| @@ -172,19 +378,15 @@ void MainContentComponent::buttonClicked (Button* b) | |||
| void MainContentComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) | |||
| { | |||
| #if JUCE_IOS | |||
| if (comboBoxThatHasChanged == &optionalParamsOneView.fireInComboBox) | |||
| if (comboBoxThatHasChanged == ¶mControls.fireInComboBox) | |||
| { | |||
| const bool repeatsAllowed = optionalParamsOneView.fireInComboBox.getSelectedItemIndex() >= 6; | |||
| const bool repeatsAllowed = paramControls.fireInComboBox.getSelectedItemIndex() >= 6; | |||
| optionalParamsOneView.repeatButton.setEnabled (repeatsAllowed); | |||
| paramControls.repeatButton.setEnabled (repeatsAllowed); | |||
| if (! repeatsAllowed) | |||
| optionalParamsOneView.repeatButton.setToggleState (false, NotificationType::sendNotification); | |||
| paramControls.repeatButton.setToggleState (false, NotificationType::sendNotification); | |||
| } | |||
| #else | |||
| ignoreUnused (comboBoxThatHasChanged); | |||
| #endif | |||
| } | |||
| void MainContentComponent::sendLocalNotification() | |||
| @@ -222,23 +424,32 @@ void MainContentComponent::sendLocalNotification() | |||
| void MainContentComponent::fillRequiredParams (PushNotifications::Notification& n) | |||
| { | |||
| n.identifier = requiredParamsView.identifierEditor.getText(); | |||
| n.title = requiredParamsView.titleEditor.getText(); | |||
| n.body = requiredParamsView.bodyEditor.getText(); | |||
| n.identifier = paramControls.identifierEditor.getText(); | |||
| n.title = paramControls.titleEditor.getText(); | |||
| n.body = paramControls.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"; | |||
| n.category = paramControls.categoryComboBox.getText(); | |||
| #elif JUCE_ANDROID || JUCE_MAC | |||
| #if JUCE_MAC | |||
| String prefix = "images/"; | |||
| String extension = ".png"; | |||
| #else | |||
| String prefix; | |||
| String extension; | |||
| #endif | |||
| if (paramControls.iconComboBox.getSelectedItemIndex() == 0) | |||
| n.icon = prefix + "ic_stat_name" + extension; | |||
| else if (paramControls.iconComboBox.getSelectedItemIndex() == 1) | |||
| n.icon = prefix + "ic_stat_name2" + extension; | |||
| else if (paramControls.iconComboBox.getSelectedItemIndex() == 2) | |||
| n.icon = prefix + "ic_stat_name3" + extension; | |||
| else if (paramControls.iconComboBox.getSelectedItemIndex() == 3) | |||
| n.icon = prefix + "ic_stat_name4" + extension; | |||
| else if (paramControls.iconComboBox.getSelectedItemIndex() == 4) | |||
| n.icon = prefix + "ic_stat_name5" + extension; | |||
| #endif | |||
| #if JUCE_ANDROID | |||
| // 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"; | |||
| @@ -247,82 +458,84 @@ void MainContentComponent::fillRequiredParams (PushNotifications::Notification& | |||
| n.publicVersion->icon = n.icon; | |||
| #if __ANDROID_API__ >= 26 | |||
| n.channelId = String (requiredParamsView.channelIdComboBox.getSelectedItemIndex() + 1); | |||
| n.channelId = String (paramControls.channelIdComboBox.getSelectedItemIndex() + 1); | |||
| #endif | |||
| #endif | |||
| } | |||
| void MainContentComponent::fillOptionalParamsOne (PushNotifications::Notification& n) | |||
| { | |||
| n.subtitle = optionalParamsOneView.subtitleEditor.getText(); | |||
| n.badgeNumber = optionalParamsOneView.badgeNumberComboBox.getSelectedItemIndex(); | |||
| n.subtitle = paramControls.subtitleEditor.getText(); | |||
| n.badgeNumber = paramControls.badgeNumberComboBox.getSelectedItemIndex(); | |||
| if (optionalParamsOneView.soundToPlayComboBox.getSelectedItemIndex() > 0) | |||
| n.soundToPlay = URL (optionalParamsOneView.soundToPlayComboBox.getItemText (optionalParamsOneView.soundToPlayComboBox.getSelectedItemIndex())); | |||
| if (paramControls.soundToPlayComboBox.getSelectedItemIndex() > 0) | |||
| n.soundToPlay = URL (paramControls.soundToPlayComboBox.getItemText (paramControls.soundToPlayComboBox.getSelectedItemIndex())); | |||
| n.properties = JSON::parse (optionalParamsOneView.propertiesEditor.getText()); | |||
| n.properties = JSON::parse (paramControls.propertiesEditor.getText()); | |||
| #if JUCE_IOS | |||
| n.triggerIntervalSec = double (optionalParamsOneView.fireInComboBox.getSelectedItemIndex() * 10); | |||
| n.repeat = optionalParamsOneView.repeatButton.getToggleState(); | |||
| #if JUCE_IOS || JUCE_MAC | |||
| n.triggerIntervalSec = double (paramControls.fireInComboBox.getSelectedItemIndex() * 10); | |||
| n.repeat = paramControls.repeatButton.getToggleState(); | |||
| #elif JUCE_ANDROID | |||
| if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 1) | |||
| if (paramControls.largeIconComboBox.getSelectedItemIndex() == 1) | |||
| n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name6_png, BinaryData::ic_stat_name6_pngSize); | |||
| else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 2) | |||
| else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 2) | |||
| n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name7_png, BinaryData::ic_stat_name7_pngSize); | |||
| else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 3) | |||
| else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 3) | |||
| n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name8_png, BinaryData::ic_stat_name8_pngSize); | |||
| else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 4) | |||
| else if (paramControls.largeIconComboBox.getSelectedItemIndex() == 4) | |||
| n.largeIcon = ImageFileFormat::loadFrom (BinaryData::ic_stat_name9_png, BinaryData::ic_stat_name9_pngSize); | |||
| else if (optionalParamsOneView.largeIconComboBox.getSelectedItemIndex() == 5) | |||
| else if (paramControls.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.badgeIconType = (PushNotifications::Notification::BadgeIconType) paramControls.badgeIconComboBox.getSelectedItemIndex(); | |||
| n.tickerText = paramControls.tickerTextEditor.getText(); | |||
| n.shouldAutoCancel = optionalParamsOneView.autoCancelButton.getToggleState(); | |||
| n.alertOnlyOnce = optionalParamsOneView.alertOnlyOnceButton.getToggleState(); | |||
| n.shouldAutoCancel = paramControls.autoCancelButton.getToggleState(); | |||
| n.alertOnlyOnce = paramControls.alertOnlyOnceButton.getToggleState(); | |||
| #endif | |||
| if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 1) | |||
| #if JUCE_ANDROID || JUCE_MAC | |||
| if (paramControls.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"; | |||
| a .title = a .identifier = "Ok"; | |||
| a2.title = a2.identifier = "Cancel"; | |||
| n.actions.add (a); | |||
| n.actions.add (a2); | |||
| } | |||
| else if (optionalParamsOneView.actionsComboBox.getSelectedItemIndex() == 2) | |||
| else if (paramControls.actionsComboBox.getSelectedItemIndex() == 2) | |||
| { | |||
| PushNotifications::Notification::Action a, a2; | |||
| a .title = "Ok"; | |||
| a2.title = "Cancel"; | |||
| a .style = PushNotifications::Notification::Action::button; | |||
| a .title = a .identifier = "Input Text Here"; | |||
| a2.title = a2.identifier = "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() == 3) | |||
| else if (paramControls.actionsComboBox.getSelectedItemIndex() == 3) | |||
| { | |||
| PushNotifications::Notification::Action a, a2; | |||
| a .title = "Input Text Here"; | |||
| a2.title = "No"; | |||
| a .style = PushNotifications::Notification::Action::text; | |||
| a .title = a .identifier = "Ok"; | |||
| a2.title = a2.identifier = "Cancel"; | |||
| a .style = PushNotifications::Notification::Action::button; | |||
| 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) | |||
| else if (paramControls.actionsComboBox.getSelectedItemIndex() == 4) | |||
| { | |||
| PushNotifications::Notification::Action a, a2; | |||
| a .title = "Input Text Here"; | |||
| a2.title = "No"; | |||
| a .title = a .identifier = "Input Text Here"; | |||
| a2.title = a2.identifier = "No"; | |||
| a .style = PushNotifications::Notification::Action::text; | |||
| a2.style = PushNotifications::Notification::Action::button; | |||
| a .icon = "ic_stat_name4"; | |||
| @@ -342,102 +555,102 @@ void MainContentComponent::fillOptionalParamsTwo (PushNotifications::Notificatio | |||
| using Notification = PushNotifications::Notification; | |||
| Notification::Progress progress; | |||
| progress.max = optionalParamsTwoView.progressMaxComboBox.getSelectedItemIndex() * 10; | |||
| progress.current = optionalParamsTwoView.progressCurrentComboBox.getSelectedItemIndex() * 10; | |||
| progress.indeterminate = optionalParamsTwoView.progressIndeterminateButton.getToggleState(); | |||
| progress.max = paramControls.progressMaxComboBox.getSelectedItemIndex() * 10; | |||
| progress.current = paramControls.progressCurrentComboBox.getSelectedItemIndex() * 10; | |||
| progress.indeterminate = paramControls.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()); | |||
| n.person = paramControls.personEditor.getText(); | |||
| n.type = Notification::Type (paramControls.categoryComboBox.getSelectedItemIndex()); | |||
| n.priority = Notification::Priority (paramControls.priorityComboBox.getSelectedItemIndex() - 2); | |||
| n.lockScreenAppearance = Notification::LockScreenAppearance (paramControls.lockScreenVisibilityComboBox.getSelectedItemIndex() - 1); | |||
| n.groupId = paramControls.groupIdEditor.getText(); | |||
| n.groupSortKey = paramControls.sortKeyEditor.getText(); | |||
| n.groupSummary = paramControls.groupSummaryButton.getToggleState(); | |||
| n.groupAlertBehaviour = Notification::GroupAlertBehaviour (paramControls.groupAlertBehaviourComboBox.getSelectedItemIndex()); | |||
| } | |||
| void MainContentComponent::fillOptionalParamsThree (PushNotifications::Notification& n) | |||
| { | |||
| n.accentColour = optionalParamsThreeView.accentColourButton.findColour (TextButton::buttonColourId, false); | |||
| n.ledColour = optionalParamsThreeView.ledColourButton .findColour (TextButton::buttonColourId, false); | |||
| n.accentColour = paramControls.accentColourButton.findColour (TextButton::buttonColourId, false); | |||
| n.ledColour = paramControls.ledColourButton .findColour (TextButton::buttonColourId, false); | |||
| using Notification = PushNotifications::Notification; | |||
| Notification::LedBlinkPattern ledBlinkPattern; | |||
| ledBlinkPattern.msToBeOn = optionalParamsThreeView.ledMsToBeOnComboBox .getSelectedItemIndex() * 200; | |||
| ledBlinkPattern.msToBeOff = optionalParamsThreeView.ledMsToBeOffComboBox.getSelectedItemIndex() * 200; | |||
| ledBlinkPattern.msToBeOn = paramControls.ledMsToBeOnComboBox .getSelectedItemIndex() * 200; | |||
| ledBlinkPattern.msToBeOff = paramControls.ledMsToBeOffComboBox.getSelectedItemIndex() * 200; | |||
| n.ledBlinkPattern = ledBlinkPattern; | |||
| Array<int> vibrationPattern; | |||
| if (optionalParamsThreeView.vibratorMsToBeOnComboBox .getSelectedItemIndex() > 0 && | |||
| optionalParamsThreeView.vibratorMsToBeOffComboBox.getSelectedItemIndex() > 0) | |||
| if (paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() > 0 && | |||
| paramControls.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); | |||
| vibrationPattern.add (paramControls.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500); | |||
| vibrationPattern.add (paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500); | |||
| vibrationPattern.add (2 * paramControls.vibratorMsToBeOffComboBox.getSelectedItemIndex() * 500); | |||
| vibrationPattern.add (2 * paramControls.vibratorMsToBeOnComboBox .getSelectedItemIndex() * 500); | |||
| } | |||
| n.vibrationPattern = vibrationPattern; | |||
| n.localOnly = optionalParamsThreeView.localOnlyButton.getToggleState(); | |||
| n.ongoing = optionalParamsThreeView.ongoingButton.getToggleState(); | |||
| n.timestampVisibility = Notification::TimestampVisibility (optionalParamsThreeView.timestampVisibilityComboBox.getSelectedItemIndex()); | |||
| n.localOnly = paramControls.localOnlyButton.getToggleState(); | |||
| n.ongoing = paramControls.ongoingButton.getToggleState(); | |||
| n.timestampVisibility = Notification::TimestampVisibility (paramControls.timestampVisibilityComboBox.getSelectedItemIndex()); | |||
| if (optionalParamsThreeView.timeoutAfterComboBox.getSelectedItemIndex() > 0) | |||
| if (paramControls.timeoutAfterComboBox.getSelectedItemIndex() > 0) | |||
| { | |||
| auto index = optionalParamsThreeView.timeoutAfterComboBox.getSelectedItemIndex(); | |||
| auto index = paramControls.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); | |||
| paramControls.accentColourSelector = new ColourSelector(); | |||
| paramControls.accentColourSelector->setName ("accent colour"); | |||
| paramControls.accentColourSelector->setCurrentColour (paramControls.accentColourButton.findColour (TextButton::buttonColourId)); | |||
| paramControls.accentColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||
| paramControls.accentColourSelector->setSize (200, 200); | |||
| paramControls.accentColourSelector->addComponentListener (this); | |||
| paramControls.accentColourSelector->addChangeListener (this); | |||
| CallOutBox::launchAsynchronously (paramControls.accentColourSelector, paramControls.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); | |||
| paramControls.ledColourSelector = new ColourSelector(); | |||
| paramControls.ledColourSelector->setName ("led colour"); | |||
| paramControls.ledColourSelector->setCurrentColour (paramControls.ledColourButton.findColour (TextButton::buttonColourId)); | |||
| paramControls.ledColourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||
| paramControls.ledColourSelector->setSize (200, 200); | |||
| paramControls.ledColourSelector->addComponentListener (this); | |||
| paramControls.ledColourSelector->addChangeListener (this); | |||
| CallOutBox::launchAsynchronously (paramControls.ledColourSelector, paramControls.accentColourButton.getScreenBounds(), nullptr); | |||
| } | |||
| void MainContentComponent::changeListenerCallback (ChangeBroadcaster* source) | |||
| { | |||
| if (source == optionalParamsThreeView.accentColourSelector) | |||
| if (source == paramControls.accentColourSelector) | |||
| { | |||
| Colour c = optionalParamsThreeView.accentColourSelector->getCurrentColour(); | |||
| optionalParamsThreeView.accentColourButton.setColour (TextButton::buttonColourId, c); | |||
| Colour c = paramControls.accentColourSelector->getCurrentColour(); | |||
| paramControls.accentColourButton.setColour (TextButton::buttonColourId, c); | |||
| } | |||
| else if (source == optionalParamsThreeView.ledColourSelector) | |||
| else if (source == paramControls.ledColourSelector) | |||
| { | |||
| Colour c = optionalParamsThreeView.ledColourSelector->getCurrentColour(); | |||
| optionalParamsThreeView.ledColourButton.setColour (TextButton::buttonColourId, c); | |||
| Colour c = paramControls.ledColourSelector->getCurrentColour(); | |||
| paramControls.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; | |||
| if (&component == paramControls.accentColourSelector) | |||
| paramControls.accentColourSelector = nullptr; | |||
| else if (&component == paramControls.ledColourSelector) | |||
| paramControls.ledColourSelector = nullptr; | |||
| } | |||
| void MainContentComponent::handleNotification (bool isLocalNotification, const PushNotifications::Notification& n) | |||
| @@ -576,9 +789,15 @@ Array<PushNotifications::Channel> MainContentComponent::getAndroidChannels() | |||
| return { ch1, ch2, ch3 }; | |||
| } | |||
| #elif JUCE_IOS | |||
| PushNotifications::Settings MainContentComponent::getIosSettings() | |||
| #elif JUCE_IOS || JUCE_MAC | |||
| PushNotifications::Settings MainContentComponent::getNotificationSettings() | |||
| { | |||
| PushNotifications::Settings settings; | |||
| settings.allowAlert = true; | |||
| settings.allowBadge = true; | |||
| settings.allowSound = true; | |||
| #if JUCE_IOS | |||
| using Action = PushNotifications::Settings::Action; | |||
| using Category = PushNotifications::Settings::Category; | |||
| @@ -617,11 +836,8 @@ PushNotifications::Settings MainContentComponent::getIosSettings() | |||
| textCategory.actions = { textAction }; | |||
| textCategory.sendDismissAction = true; | |||
| PushNotifications::Settings settings; | |||
| settings.allowAlert = true; | |||
| settings.allowBadge = true; | |||
| settings.allowSound = true; | |||
| settings.categories = { okCategory, okCancelCategory, textCategory }; | |||
| #endif | |||
| return settings; | |||
| } | |||
| @@ -88,230 +88,61 @@ private: | |||
| void upstreamMessageSendingError (const String& messageId, const String& error) override; | |||
| static Array<PushNotifications::Channel> getAndroidChannels(); | |||
| #elif JUCE_IOS | |||
| static PushNotifications::Settings getIosSettings(); | |||
| #elif JUCE_IOS || JUCE_MAC | |||
| static PushNotifications::Settings getNotificationSettings(); | |||
| #endif | |||
| struct RequiredParamsView : public Component | |||
| struct RowComponent : public Component | |||
| { | |||
| RequiredParamsView() | |||
| RowComponent (Label& l, Component& c, int u = 1) | |||
| : label (l), | |||
| editor (c), | |||
| rowUnits (u) | |||
| { | |||
| 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); | |||
| addAndMakeVisible (label); | |||
| addAndMakeVisible (editor); | |||
| } | |||
| 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 .setBounds (bounds.removeFromLeft (getWidth() / 3)); | |||
| editor.setBounds (bounds); | |||
| } | |||
| 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 | |||
| Label& label; | |||
| Component& editor; | |||
| int rowUnits; | |||
| }; | |||
| struct OptionalParamsOneView : public Component | |||
| struct ParamControls | |||
| { | |||
| 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 identifierLabel { "identifierLabel", "Identifier" }; | |||
| TextEditor identifierEditor; | |||
| Label titleLabel { "titleLabel", "Title" }; | |||
| TextEditor titleEditor; | |||
| Label bodyLabel { "bodyLabel", "Body" }; | |||
| TextEditor bodyEditor; | |||
| Label categoryLabel { "categoryLabel", "Category" }; | |||
| ComboBox categoryComboBox; | |||
| Label channelIdLabel { "channelIdLabel", "Channel ID" }; | |||
| ComboBox channelIdComboBox; | |||
| Label iconLabel { "iconLabel", "Icon" }; | |||
| ComboBox iconComboBox; | |||
| Label subtitleLabel { "subtitleLabel", "Subtitle" }; | |||
| TextEditor subtitleEditor; | |||
| Label badgeNumberLabel { "badgeNumberLabel", "BadgeNumber" }; | |||
| ComboBox badgeNumberComboBox; | |||
| Label soundToPlayLabel { "soundToPlayLabel", "SoundToPlay" }; | |||
| ComboBox soundToPlayComboBox; | |||
| Label propertiesLabel { "propertiesLabel", "Properties" }; | |||
| TextEditor propertiesEditor; | |||
| 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" }; | |||
| @@ -324,106 +155,6 @@ private: | |||
| 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; | |||
| @@ -431,8 +162,8 @@ private: | |||
| ComboBox progressCurrentComboBox; | |||
| Label progressIndeterminateLabel { "progressIndeterminateLabel", "ProgressIndeterminate" }; | |||
| ToggleButton progressIndeterminateButton; | |||
| Label categoryLabel { "categoryLabel", "Category" }; | |||
| ComboBox categoryComboBox; | |||
| Label notifCategoryLabel { "notifCategoryLabel", "Category" }; | |||
| ComboBox notifCategoryComboBox; | |||
| Label priorityLabel { "priorityLabel", "Priority" }; | |||
| ComboBox priorityComboBox; | |||
| Label personLabel { "personLabel", "Person" }; | |||
| @@ -447,84 +178,6 @@ private: | |||
| 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; | |||
| @@ -551,6 +204,45 @@ private: | |||
| ColourSelector* ledColourSelector = nullptr; | |||
| }; | |||
| void setupControls(); | |||
| void distributeControls(); | |||
| struct ParamsView : public Component | |||
| { | |||
| ParamsView() | |||
| { | |||
| // For now, to be able to dismiss mobile keyboard. | |||
| setWantsKeyboardFocus (true); | |||
| } | |||
| void addRowComponent (RowComponent *rc) | |||
| { | |||
| rowComponents.add (rc); | |||
| addAndMakeVisible (rc); | |||
| } | |||
| void resized() override | |||
| { | |||
| int totalRowUnits = 0; | |||
| for (const auto &rc : rowComponents) | |||
| totalRowUnits += rc->rowUnits; | |||
| const int rowHeight = getHeight() / totalRowUnits; | |||
| auto bounds = getLocalBounds(); | |||
| for (auto &rc : rowComponents) | |||
| rc->setBounds (bounds.removeFromTop (rc->rowUnits * rowHeight)); | |||
| auto* last = rowComponents[rowComponents.size() - 1]; | |||
| last->setBounds (last->getBounds().withHeight (getHeight() - last->getY())); | |||
| } | |||
| private: | |||
| OwnedArray<RowComponent> rowComponents; | |||
| }; | |||
| struct AuxActionsView : public Component | |||
| { | |||
| AuxActionsView() | |||
| @@ -559,7 +251,7 @@ private: | |||
| addAndMakeVisible (removeDeliveredNotifWithIdButton); | |||
| addAndMakeVisible (deliveredNotifIdentifier); | |||
| addAndMakeVisible (removeAllDeliveredNotifsButton); | |||
| #if JUCE_IOS | |||
| #if JUCE_IOS || JUCE_MAC | |||
| addAndMakeVisible (getPendingNotificationsButton); | |||
| addAndMakeVisible (removePendingNotifWithIdButton); | |||
| addAndMakeVisible (pendingNotifIdentifier); | |||
| @@ -586,7 +278,7 @@ private: | |||
| removeAllDeliveredNotifsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||
| #if JUCE_IOS | |||
| #if JUCE_IOS || JUCE_MAC | |||
| getPendingNotificationsButton .setBounds (bounds.removeFromTop (rowHeight)); | |||
| rowBounds = bounds.removeFromTop (rowHeight); | |||
| @@ -601,12 +293,10 @@ private: | |||
| 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 | |||
| @@ -641,17 +331,52 @@ private: | |||
| 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!" }; | |||
| struct DemoTabbedComponent : public TabbedComponent | |||
| { | |||
| explicit DemoTabbedComponent (TabbedButtonBar::Orientation orientation) | |||
| : TabbedComponent (orientation) | |||
| { | |||
| } | |||
| void currentTabChanged (int, const String& newCurrentTabName) override | |||
| { | |||
| if (! showedRemoteInstructions && newCurrentTabName == "Remote") | |||
| { | |||
| MainContentComponent::showRemoteInstructions(); | |||
| showedRemoteInstructions = true; | |||
| } | |||
| } | |||
| private: | |||
| bool showedRemoteInstructions = false; | |||
| }; | |||
| static void showRemoteInstructions() | |||
| { | |||
| #if JUCE_IOS || JUCE_MAC | |||
| NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, | |||
| "Remote Notifications instructions", | |||
| "In order to be able to test remote notifications " | |||
| "ensure that the app is signed and that you register " | |||
| "the bundle ID for remote notifications in " | |||
| "Apple Developer Center."); | |||
| #endif | |||
| } | |||
| Label headerLabel { "headerLabel", "Push Notifications Demo" }; | |||
| ParamControls paramControls; | |||
| ParamsView paramsOneView; | |||
| ParamsView paramsTwoView; | |||
| ParamsView paramsThreeView; | |||
| ParamsView paramsFourView; | |||
| AuxActionsView auxActionsView; | |||
| TabbedComponent localNotificationsTabs { TabbedButtonBar::TabsAtTop }; | |||
| RemoteView remoteView; | |||
| DemoTabbedComponent 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) | |||
| @@ -249,9 +249,6 @@ public: | |||
| props.add (new BooleanPropertyComponent (getBackgroundBleValue(), "Bluetooth MIDI background capability", "Enabled"), | |||
| "Enable this to grant your app the capability to connect to Bluetooth LE devices when in background mode."); | |||
| props.add (new BooleanPropertyComponent (getPushNotificationsValue(), "Push Notifications capability", "Enabled"), | |||
| "Enable this to grant your app the capability to receive push notifications."); | |||
| props.add (new BooleanPropertyComponent (getAppGroupsEnabledValue(), "App groups capability", "Enabled"), | |||
| "Enable this to grant your app the capability to share resources between apps using the same app group ID."); | |||
| @@ -259,6 +256,9 @@ public: | |||
| "Enable this to grant your app the capability to use native file load/save browser windows on iOS."); | |||
| } | |||
| props.add (new BooleanPropertyComponent (getPushNotificationsValue(), "Push Notifications capability", "Enabled"), | |||
| "Enable this to grant your app the capability to receive push notifications."); | |||
| props.add (new TextPropertyComponent (getPListToMergeValue(), "Custom PList", 8192, true), | |||
| "You can paste the contents of an XML PList file in here, and the settings that it contains will override any " | |||
| "settings that the Projucer creates. BEWARE! When doing this, be careful to remove from the XML any " | |||
| @@ -860,7 +860,7 @@ public: | |||
| && type == Target::StandalonePlugIn | |||
| && owner.getProject().shouldEnableIAA()) ? 1 : 0; | |||
| auto pushNotificationsEnabled = (owner.iOS && owner.isPushNotificationsEnabled()) ? 1 : 0; | |||
| auto pushNotificationsEnabled = owner.isPushNotificationsEnabled() ? 1 : 0; | |||
| auto sandboxEnabled = (type == Target::AudioUnitv3PlugIn ? 1 : 0); | |||
| attributes << "SystemCapabilities = {"; | |||
| @@ -1146,7 +1146,7 @@ public: | |||
| if (owner.isInAppPurchasesEnabled()) | |||
| defines.set ("JUCE_IN_APP_PURCHASES", "1"); | |||
| if (owner.iOS && owner.isPushNotificationsEnabled()) | |||
| if (owner.isPushNotificationsEnabled()) | |||
| defines.set ("JUCE_PUSH_NOTIFICATIONS", "1"); | |||
| defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type)); | |||
| @@ -2552,8 +2552,10 @@ private: | |||
| } | |||
| else | |||
| { | |||
| if (isiOS() && isPushNotificationsEnabled()) | |||
| entitlements.set ("aps-environment", "<string>development</string>"); | |||
| if (isPushNotificationsEnabled()) | |||
| entitlements.set (isiOS() ? "aps-environment" | |||
| : "com.apple.developer.aps-environment", | |||
| "<string>development</string>"); | |||
| } | |||
| if (isAppGroupsEnabled()) | |||
| @@ -67,6 +67,132 @@ static inline NSArray* createNSArrayFromStringArray (const StringArray& strings) | |||
| return [array autorelease]; | |||
| } | |||
| static NSArray* varArrayToNSArray (const var& varToParse); | |||
| static NSDictionary* varObjectToNSDictionary (const var& varToParse) | |||
| { | |||
| auto* dictionary = [NSMutableDictionary dictionary]; | |||
| if (varToParse.isObject()) | |||
| { | |||
| auto* dynamicObject = varToParse.getDynamicObject(); | |||
| auto& properties = dynamicObject->getProperties(); | |||
| for (int i = 0; i < properties.size(); ++i) | |||
| { | |||
| auto* keyString = juceStringToNS (properties.getName (i).toString()); | |||
| const var& valueVar = properties.getValueAt (i); | |||
| if (valueVar.isObject()) | |||
| { | |||
| auto* valueDictionary = varObjectToNSDictionary (valueVar); | |||
| [dictionary setObject: valueDictionary forKey: keyString]; | |||
| } | |||
| else if (valueVar.isArray()) | |||
| { | |||
| auto* valueArray = varArrayToNSArray (valueVar); | |||
| [dictionary setObject: valueArray forKey: keyString]; | |||
| } | |||
| else | |||
| { | |||
| auto* valueString = juceStringToNS (valueVar.toString()); | |||
| [dictionary setObject: valueString forKey: keyString]; | |||
| } | |||
| } | |||
| } | |||
| return dictionary; | |||
| } | |||
| static NSArray* varArrayToNSArray (const var& varToParse) | |||
| { | |||
| jassert (varToParse.isArray()); | |||
| if (! varToParse.isArray()) | |||
| return nil; | |||
| const auto* varArray = varToParse.getArray(); | |||
| auto* array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()]; | |||
| for (const auto& aVar : *varArray) | |||
| { | |||
| if (aVar.isObject()) | |||
| { | |||
| auto* valueDictionary = varObjectToNSDictionary (aVar); | |||
| [array addObject: valueDictionary]; | |||
| } | |||
| else if (aVar.isArray()) | |||
| { | |||
| auto* valueArray = varArrayToNSArray (aVar); | |||
| [array addObject: valueArray]; | |||
| } | |||
| else | |||
| { | |||
| auto* valueString = juceStringToNS (aVar.toString()); | |||
| [array addObject: valueString]; | |||
| } | |||
| } | |||
| return array; | |||
| } | |||
| static var nsArrayToVar (NSArray* array); | |||
| static var nsDictionaryToVar (NSDictionary* dictionary) | |||
| { | |||
| DynamicObject::Ptr dynamicObject = new DynamicObject(); | |||
| for (NSString* key in dictionary) | |||
| { | |||
| const auto keyString = nsStringToJuce (key); | |||
| id value = dictionary[key]; | |||
| if ([value isKindOfClass: [NSString class]]) | |||
| dynamicObject->setProperty (keyString, nsStringToJuce ((NSString*) value)); | |||
| else if ([value isKindOfClass: [NSNumber class]]) | |||
| dynamicObject->setProperty (keyString, nsStringToJuce ([(NSNumber*) value stringValue])); | |||
| else if ([value isKindOfClass: [NSDictionary class]]) | |||
| dynamicObject->setProperty (keyString, nsDictionaryToVar ((NSDictionary*) value)); | |||
| else if ([value isKindOfClass: [NSArray class]]) | |||
| dynamicObject->setProperty (keyString, nsArrayToVar ((NSArray*) value)); | |||
| else | |||
| jassertfalse; // Unsupported yet, add here! | |||
| } | |||
| return var (dynamicObject.get()); | |||
| } | |||
| static var nsArrayToVar (NSArray* array) | |||
| { | |||
| Array<var> resultArray; | |||
| for (id value in array) | |||
| { | |||
| if ([value isKindOfClass: [NSString class]]) | |||
| resultArray.add (var (nsStringToJuce ((NSString*) value))); | |||
| else if ([value isKindOfClass: [NSNumber class]]) | |||
| resultArray.add (var (nsStringToJuce ([(NSNumber*) value stringValue]))); | |||
| else if ([value isKindOfClass: [NSDictionary class]]) | |||
| resultArray.add (nsDictionaryToVar ((NSDictionary*) value)); | |||
| else if ([value isKindOfClass: [NSArray class]]) | |||
| resultArray.add (nsArrayToVar ((NSArray*) value)); | |||
| else | |||
| jassertfalse; // Unsupported yet, add here! | |||
| } | |||
| return var (resultArray); | |||
| } | |||
| #if JUCE_MAC | |||
| template <typename RectangleType> | |||
| static NSRect makeNSRect (const RectangleType& r) noexcept | |||
| @@ -102,7 +102,7 @@ private: | |||
| { | |||
| AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_") | |||
| { | |||
| addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@@"); | |||
| addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@"); | |||
| addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); | |||
| addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); | |||
| addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); | |||
| @@ -116,11 +116,22 @@ private: | |||
| addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); | |||
| addMethod (@selector (dummyMethod), dummyMethod, "v@:"); | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| //============================================================================== | |||
| addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate"); | |||
| addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); | |||
| addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@"); | |||
| addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); | |||
| addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); | |||
| addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); | |||
| #endif | |||
| registerClass(); | |||
| } | |||
| private: | |||
| static void applicationWillFinishLaunching (id self, SEL, NSApplication*, NSNotification*) | |||
| static void applicationWillFinishLaunching (id self, SEL, NSNotification*) | |||
| { | |||
| [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self | |||
| andSelector: @selector (getUrl:withReplyEvent:) | |||
| @@ -128,6 +139,19 @@ private: | |||
| andEventID: kAEGetURL]; | |||
| } | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification) | |||
| { | |||
| if (notification.userInfo != nil) | |||
| { | |||
| NSUserNotification* userNotification = [notification.userInfo objectForKey: nsStringLiteral ("NSApplicationLaunchUserNotificationKey")]; | |||
| if (userNotification != nil && userNotification.userInfo != nil) | |||
| didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); | |||
| } | |||
| } | |||
| #endif | |||
| static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) | |||
| { | |||
| if (auto* app = JUCEApplicationBase::getInstance()) | |||
| @@ -216,6 +240,73 @@ private: | |||
| return s; | |||
| } | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| //============================================================================== | |||
| static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate) | |||
| { | |||
| object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); | |||
| } | |||
| static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self) | |||
| { | |||
| return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate"); | |||
| } | |||
| static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken) | |||
| { | |||
| auto* delegate = getPushNotificationsDelegate (self); | |||
| SEL selector = NSSelectorFromString (@"application:didRegisterForRemoteNotificationsWithDeviceToken:"); | |||
| if (delegate != nil && [delegate respondsToSelector: selector]) | |||
| { | |||
| NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; | |||
| [invocation setSelector: selector]; | |||
| [invocation setTarget: delegate]; | |||
| [invocation setArgument: &application atIndex:2]; | |||
| [invocation setArgument: &deviceToken atIndex:3]; | |||
| [invocation invoke]; | |||
| } | |||
| } | |||
| static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error) | |||
| { | |||
| auto* delegate = getPushNotificationsDelegate (self); | |||
| SEL selector = NSSelectorFromString (@"application:didFailToRegisterForRemoteNotificationsWithError:"); | |||
| if (delegate != nil && [delegate respondsToSelector: selector]) | |||
| { | |||
| NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; | |||
| [invocation setSelector: selector]; | |||
| [invocation setTarget: delegate]; | |||
| [invocation setArgument: &application atIndex:2]; | |||
| [invocation setArgument: &error atIndex:3]; | |||
| [invocation invoke]; | |||
| } | |||
| } | |||
| static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo) | |||
| { | |||
| auto* delegate = getPushNotificationsDelegate (self); | |||
| SEL selector = NSSelectorFromString (@"application:didReceiveRemoteNotification:"); | |||
| if (delegate != nil && [delegate respondsToSelector: selector]) | |||
| { | |||
| NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; | |||
| [invocation setSelector: selector]; | |||
| [invocation setTarget: delegate]; | |||
| [invocation setArgument: &application atIndex:2]; | |||
| [invocation setArgument: &userInfo atIndex:3]; | |||
| [invocation invoke]; | |||
| } | |||
| } | |||
| #endif | |||
| }; | |||
| }; | |||
| @@ -55,6 +55,12 @@ | |||
| #import <IOKit/hid/IOHIDKeys.h> | |||
| #import <IOKit/pwr_mgt/IOPMLib.h> | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| #import <Foundation/NSUserNotification.h> | |||
| #include "native/juce_mac_PushNotifications.cpp" | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_IOS | |||
| #if JUCE_PUSH_NOTIFICATIONS | |||
| @@ -28,7 +28,7 @@ namespace juce | |||
| { | |||
| //============================================================================== | |||
| #if ! JUCE_ANDROID && ! JUCE_IOS | |||
| #if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC | |||
| bool PushNotifications::Notification::isValid() const noexcept { return true; } | |||
| #endif | |||
| @@ -49,7 +49,7 @@ void PushNotifications::removeListener (Listener* l) { listeners.remove (l); } | |||
| void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings) | |||
| { | |||
| #if JUCE_PUSH_NOTIFICATIONS && JUCE_IOS | |||
| #if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC) | |||
| pimpl->requestPermissionsWithSettings (settings); | |||
| #else | |||
| ignoreUnused (settings); | |||
| @@ -59,7 +59,7 @@ void PushNotifications::requestPermissionsWithSettings (const PushNotifications: | |||
| void PushNotifications::requestSettingsUsed() | |||
| { | |||
| #if JUCE_PUSH_NOTIFICATIONS && JUCE_IOS | |||
| #if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC) | |||
| pimpl->requestSettingsUsed(); | |||
| #else | |||
| listeners.call (&PushNotifications::Listener::notificationSettingsReceived, {}); | |||
| @@ -124,6 +124,14 @@ public: | |||
| 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 OSX, set the URL to the name of a sound file (preferably without | |||
| an extension) and place the sound file directly in bundle's "Resources" directory (you | |||
| can use "Xcode Resource" tickbox in Projucer to achieve that), i.e. it cannot be in a | |||
| subdirectory of "Resources" like "Resources/sound". Alternatively, if a sound file | |||
| cannot be found in bundle's "Resources" directory, the OS may look for the sound in the | |||
| following paths: "~/Library/Sounds", "/Library/Sounds", "/Network/Library/Sounds", | |||
| "/System/Library/Sounds". | |||
| 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". | |||
| @@ -312,7 +320,11 @@ public: | |||
| //========================================================================== | |||
| /** Describes settings we want to use for current device. Note that at the | |||
| moment this is only used on iOS. | |||
| moment this is only used on iOS and partially on OSX. | |||
| On OSX only allow* flags are used and they control remote notifications only. | |||
| To control sound, alert and badge settings for local notifications on OSX, | |||
| use Notifications settings in System Preferences. | |||
| To setup push notifications for current device, provide permissions required, | |||
| as well as register categories of notifications you want to support. Each | |||
| @@ -411,9 +423,13 @@ public: | |||
| 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(). | |||
| Note that settings are currently only used on iOS and partially on OSX. | |||
| On OSX, only allow* flags are used and they refer to remote notifications only. For | |||
| local notifications, refer to System Preferences. | |||
| When calling this function on other platforms, Settings with no categories and all allow* | |||
| flags set to true will be received in Listener::notificationSettingsReceived(). | |||
| */ | |||
| void requestSettingsUsed(); | |||
| @@ -486,7 +502,7 @@ public: | |||
| //========================================================================== | |||
| /** Checks whether notifications are enabled for given application. | |||
| On iOS this will always return true, use requestSettingsUsed() instead. | |||
| On iOS and OSX this will always return true, use requestSettingsUsed() instead. | |||
| */ | |||
| bool areNotificationsEnabled() const; | |||
| @@ -34,84 +34,6 @@ template <> struct ContainerDeletePolicy<NSObject<UIApplicationDelegate, UNUserN | |||
| namespace PushNotificationsDelegateDetails | |||
| { | |||
| //============================================================================== | |||
| NSArray* varArrayToNSArray (const var& varToParse); | |||
| NSDictionary* varObjectToNSDictionary (const var& varToParse) | |||
| { | |||
| NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; | |||
| if (varToParse.isObject()) | |||
| { | |||
| auto* dynamicObject = varToParse.getDynamicObject(); | |||
| auto& properties = dynamicObject->getProperties(); | |||
| for (int i = 0; i < properties.size(); ++i) | |||
| { | |||
| NSString* keyString = juceStringToNS (properties.getName (i).toString()); | |||
| const var& valueVar = properties.getValueAt (i); | |||
| if (valueVar.isObject()) | |||
| { | |||
| NSDictionary* valueDictionary = varObjectToNSDictionary (valueVar); | |||
| [dictionary setObject: valueDictionary forKey: keyString]; | |||
| } | |||
| else if (valueVar.isArray()) | |||
| { | |||
| NSArray* valueArray = varArrayToNSArray (valueVar); | |||
| [dictionary setObject: valueArray forKey: keyString]; | |||
| } | |||
| else | |||
| { | |||
| NSString* valueString = juceStringToNS (valueVar.toString()); | |||
| [dictionary setObject: valueString forKey: keyString]; | |||
| } | |||
| } | |||
| } | |||
| return dictionary; | |||
| } | |||
| NSArray* varArrayToNSArray (const var& varToParse) | |||
| { | |||
| jassert (varToParse.isArray()); | |||
| if (! varToParse.isArray()) | |||
| return nil; | |||
| const auto* varArray = varToParse.getArray(); | |||
| NSMutableArray* array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()]; | |||
| for (const auto& aVar : *varArray) | |||
| { | |||
| if (aVar.isObject()) | |||
| { | |||
| NSDictionary* valueDictionary = varObjectToNSDictionary (aVar); | |||
| [array addObject: valueDictionary]; | |||
| } | |||
| else if (aVar.isArray()) | |||
| { | |||
| NSArray* valueArray = varArrayToNSArray (aVar); | |||
| [array addObject: valueArray]; | |||
| } | |||
| else | |||
| { | |||
| NSString* valueString = juceStringToNS (aVar.toString()); | |||
| [array addObject: valueString]; | |||
| } | |||
| } | |||
| return array; | |||
| } | |||
| using Action = PushNotifications::Settings::Action; | |||
| using Category = PushNotifications::Settings::Category; | |||
| @@ -210,7 +132,7 @@ namespace PushNotificationsDelegateDetails | |||
| auto triggerTime = Time::getCurrentTime() + RelativeTime (n.triggerIntervalSec); | |||
| notification.fireDate = [NSDate dateWithTimeIntervalSince1970: triggerTime.toMilliseconds() / 1000.]; | |||
| notification.userInfo = PushNotificationsDelegateDetails::varObjectToNSDictionary (n.properties); | |||
| notification.userInfo = varObjectToNSDictionary (n.properties); | |||
| auto soundToPlayString = n.soundToPlay.toString (true); | |||
| @@ -242,7 +164,7 @@ namespace PushNotificationsDelegateDetails | |||
| else if (soundToPlayString.isNotEmpty()) | |||
| content.sound = [UNNotificationSound soundNamed: juceStringToNS (soundToPlayString)]; | |||
| NSMutableDictionary* propsDict = (NSMutableDictionary*) PushNotificationsDelegateDetails::varObjectToNSDictionary (n.properties); | |||
| NSMutableDictionary* propsDict = (NSMutableDictionary*) varObjectToNSDictionary (n.properties); | |||
| [propsDict setObject: juceStringToNS (soundToPlayString) forKey: nsStringLiteral ("com.juce.soundName")]; | |||
| content.userInfo = propsDict; | |||
| @@ -268,54 +190,6 @@ namespace PushNotificationsDelegateDetails | |||
| } | |||
| #endif | |||
| var nsArrayToVar (NSArray* array); | |||
| var nsDictionaryToVar (NSDictionary* dictionary) | |||
| { | |||
| DynamicObject::Ptr dynamicObject = new DynamicObject(); | |||
| for (NSString* key in dictionary) | |||
| { | |||
| const auto keyString = nsStringToJuce (key); | |||
| id value = dictionary[key]; | |||
| if ([value isKindOfClass: [NSString class]]) | |||
| dynamicObject->setProperty (keyString, nsStringToJuce ((NSString*) value)); | |||
| else if ([value isKindOfClass: [NSNumber class]]) | |||
| dynamicObject->setProperty (keyString, nsStringToJuce ([(NSNumber*) value stringValue])); | |||
| else if ([value isKindOfClass: [NSDictionary class]]) | |||
| dynamicObject->setProperty (keyString, nsDictionaryToVar ((NSDictionary*) value)); | |||
| else if ([value isKindOfClass: [NSArray class]]) | |||
| dynamicObject->setProperty (keyString, nsArrayToVar ((NSArray*) value)); | |||
| else | |||
| jassertfalse; // Unsupported yet, add here! | |||
| } | |||
| return var (dynamicObject); | |||
| } | |||
| var nsArrayToVar (NSArray* array) | |||
| { | |||
| Array<var> resultArray; | |||
| for (id value in array) | |||
| { | |||
| if ([value isKindOfClass: [NSString class]]) | |||
| resultArray.add (var (nsStringToJuce ((NSString*) value))); | |||
| else if ([value isKindOfClass: [NSNumber class]]) | |||
| resultArray.add (var (nsStringToJuce ([(NSNumber*) value stringValue]))); | |||
| else if ([value isKindOfClass: [NSDictionary class]]) | |||
| resultArray.add (nsDictionaryToVar ((NSDictionary*) value)); | |||
| else if ([value isKindOfClass: [NSArray class]]) | |||
| resultArray.add (nsArrayToVar ((NSArray*) value)); | |||
| else | |||
| jassertfalse; // Unsupported yet, add here! | |||
| } | |||
| return var (resultArray); | |||
| } | |||
| String getUserResponseFromNSDictionary (NSDictionary* dictionary) | |||
| { | |||
| if (dictionary == nil || dictionary.count == 0) | |||
| @@ -397,7 +271,7 @@ namespace PushNotificationsDelegateDetails | |||
| n.category = nsStringToJuce (r.content.categoryIdentifier); | |||
| n.badgeNumber = r.content.badge.intValue; | |||
| auto userInfoVar = PushNotificationsDelegateDetails::nsDictionaryToVar (r.content.userInfo); | |||
| auto userInfoVar = nsDictionaryToVar (r.content.userInfo); | |||
| if (auto* object = userInfoVar.getDynamicObject()) | |||
| { | |||
| @@ -755,8 +629,6 @@ struct PushNotifications::Pimpl : private PushNotificationsDelegate | |||
| #endif | |||
| [[UIApplication sharedApplication] registerForRemoteNotifications]; | |||
| initialised = true; | |||
| } | |||
| void requestSettingsUsed() | |||
| @@ -980,6 +852,8 @@ struct PushNotifications::Pimpl : private PushNotificationsDelegate | |||
| deviceToken = nsStringToJuce (deviceTokenString); | |||
| initialised = true; | |||
| owner.listeners.call (&PushNotifications::Listener::deviceTokenRefreshed, deviceToken); | |||
| } | |||
| @@ -0,0 +1,557 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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 | |||
| { | |||
| template <> struct ContainerDeletePolicy<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>> { static void destroy (NSObject* o) { [o release]; } }; | |||
| namespace PushNotificationsDelegateDetailsOsx | |||
| { | |||
| using Action = PushNotifications::Notification::Action; | |||
| //============================================================================== | |||
| NSUserNotification* juceNotificationToNSUserNotification (const PushNotifications::Notification& n, | |||
| bool isEarlierThanMavericks, | |||
| bool isEarlierThanYosemite) | |||
| { | |||
| auto* notification = [[NSUserNotification alloc] init]; | |||
| notification.title = juceStringToNS (n.title); | |||
| notification.subtitle = juceStringToNS (n.subtitle); | |||
| notification.informativeText = juceStringToNS (n.body); | |||
| notification.userInfo = varObjectToNSDictionary (n.properties); | |||
| auto triggerTime = Time::getCurrentTime() + RelativeTime (n.triggerIntervalSec); | |||
| notification.deliveryDate = [NSDate dateWithTimeIntervalSince1970: triggerTime.toMilliseconds() / 1000.]; | |||
| if (n.repeat && n.triggerIntervalSec >= 60) | |||
| { | |||
| auto* dateComponents = [[NSDateComponents alloc] init]; | |||
| auto intervalSec = NSInteger (n.triggerIntervalSec); | |||
| dateComponents.second = intervalSec; | |||
| dateComponents.nanosecond = NSInteger ((n.triggerIntervalSec - intervalSec) * 1000000000); | |||
| notification.deliveryRepeatInterval = dateComponents; | |||
| [dateComponents autorelease]; | |||
| } | |||
| auto soundToPlayString = n.soundToPlay.toString (true); | |||
| if (soundToPlayString == "default_os_sound") | |||
| { | |||
| notification.soundName = NSUserNotificationDefaultSoundName; | |||
| } | |||
| else if (soundToPlayString.isNotEmpty()) | |||
| { | |||
| auto* soundName = juceStringToNS (soundToPlayString.fromLastOccurrenceOf ("/", false, false) | |||
| .upToLastOccurrenceOf (".", false, false)); | |||
| notification.soundName = soundName; | |||
| } | |||
| notification.hasActionButton = n.actions.size() > 0; | |||
| if (n.actions.size() > 0) | |||
| notification.actionButtonTitle = juceStringToNS (n.actions.getReference (0).title); | |||
| if (! isEarlierThanMavericks) | |||
| { | |||
| notification.identifier = juceStringToNS (n.identifier); | |||
| if (n.actions.size() > 0) | |||
| { | |||
| notification.hasReplyButton = n.actions.getReference (0).style == Action::text; | |||
| notification.responsePlaceholder = juceStringToNS (n.actions.getReference (0).textInputPlaceholder); | |||
| } | |||
| auto* imageDirectory = n.icon.contains ("/") | |||
| ? juceStringToNS (n.icon.upToLastOccurrenceOf ("/", false, true)) | |||
| : [NSString string]; | |||
| auto* imageName = juceStringToNS (n.icon.fromLastOccurrenceOf ("/", false, false) | |||
| .upToLastOccurrenceOf (".", false, false)); | |||
| auto* imageExtension = juceStringToNS (n.icon.fromLastOccurrenceOf (".", false, false)); | |||
| NSString* imagePath = nil; | |||
| if ([imageDirectory length] == NSUInteger (0)) | |||
| { | |||
| imagePath = [[NSBundle mainBundle] pathForResource: imageName | |||
| ofType: imageExtension]; | |||
| } | |||
| else | |||
| { | |||
| imagePath = [[NSBundle mainBundle] pathForResource: imageName | |||
| ofType: imageExtension | |||
| inDirectory: imageDirectory]; | |||
| } | |||
| notification.contentImage = [[NSImage alloc] initWithContentsOfFile: imagePath]; | |||
| if (! isEarlierThanYosemite) | |||
| { | |||
| if (n.actions.size() > 1) | |||
| { | |||
| auto* additionalActions = [NSMutableArray arrayWithCapacity: (NSUInteger) n.actions.size() - 1]; | |||
| for (int a = 1; a < n.actions.size(); ++a) | |||
| [additionalActions addObject: [NSUserNotificationAction actionWithIdentifier: juceStringToNS (n.actions[a].identifier) | |||
| title: juceStringToNS (n.actions[a].title)]]; | |||
| notification.additionalActions = additionalActions; | |||
| } | |||
| } | |||
| } | |||
| [notification autorelease]; | |||
| return notification; | |||
| } | |||
| //============================================================================== | |||
| PushNotifications::Notification nsUserNotificationToJuceNotification (NSUserNotification* n, | |||
| bool isEarlierThanMavericks, | |||
| bool isEarlierThanYosemite) | |||
| { | |||
| PushNotifications::Notification notif; | |||
| notif.title = nsStringToJuce (n.title); | |||
| notif.subtitle = nsStringToJuce (n.subtitle); | |||
| notif.body = nsStringToJuce (n.informativeText); | |||
| notif.repeat = n.deliveryRepeatInterval != nil; | |||
| if (n.deliveryRepeatInterval != nil) | |||
| { | |||
| notif.triggerIntervalSec = n.deliveryRepeatInterval.second + (n.deliveryRepeatInterval.nanosecond / 1000000000.); | |||
| } | |||
| else | |||
| { | |||
| NSDate* dateNow = [NSDate date]; | |||
| notif.triggerIntervalSec = [dateNow timeIntervalSinceDate: n.deliveryDate]; | |||
| } | |||
| notif.soundToPlay = URL (nsStringToJuce (n.soundName)); | |||
| notif.properties = nsDictionaryToVar (n.userInfo); | |||
| if (! isEarlierThanMavericks) | |||
| { | |||
| notif.identifier = nsStringToJuce (n.identifier); | |||
| if (n.contentImage != nil) | |||
| notif.icon = nsStringToJuce ([n.contentImage name]); | |||
| } | |||
| Array<Action> actions; | |||
| if (n.actionButtonTitle != nil) | |||
| { | |||
| Action action; | |||
| action.title = nsStringToJuce (n.actionButtonTitle); | |||
| if (! isEarlierThanMavericks) | |||
| { | |||
| if (n.hasReplyButton) | |||
| action.style = Action::text; | |||
| if (n.responsePlaceholder != nil) | |||
| action.textInputPlaceholder = nsStringToJuce (n.responsePlaceholder); | |||
| } | |||
| actions.add (action); | |||
| } | |||
| if (! isEarlierThanYosemite) | |||
| { | |||
| if (n.additionalActions != nil) | |||
| { | |||
| for (NSUserNotificationAction* a in n.additionalActions) | |||
| { | |||
| Action action; | |||
| action.identifier = nsStringToJuce (a.identifier); | |||
| action.title = nsStringToJuce (a.title); | |||
| actions.add (action); | |||
| } | |||
| } | |||
| } | |||
| return notif; | |||
| } | |||
| //============================================================================== | |||
| var getNotificationPropertiesFromDictionaryVar (const var& dictionaryVar) | |||
| { | |||
| auto* dictionaryVarObject = dictionaryVar.getDynamicObject(); | |||
| if (dictionaryVarObject == nullptr) | |||
| return {}; | |||
| const auto& properties = dictionaryVarObject->getProperties(); | |||
| DynamicObject::Ptr propsVarObject = new DynamicObject(); | |||
| for (int i = 0; i < properties.size(); ++i) | |||
| { | |||
| auto propertyName = properties.getName (i).toString(); | |||
| if (propertyName == "aps") | |||
| continue; | |||
| propsVarObject->setProperty (propertyName, properties.getValueAt (i)); | |||
| } | |||
| return var (propsVarObject.get()); | |||
| } | |||
| PushNotifications::Notification nsDictionaryToJuceNotification (NSDictionary* dictionary) | |||
| { | |||
| const var dictionaryVar = nsDictionaryToVar (dictionary); | |||
| const var apsVar = dictionaryVar.getProperty ("aps", {}); | |||
| if (! apsVar.isObject()) | |||
| return {}; | |||
| var alertVar = apsVar.getProperty ("alert", {}); | |||
| const var titleVar = alertVar.getProperty ("title", {}); | |||
| const var bodyVar = alertVar.isObject() ? alertVar.getProperty ("body", {}) : alertVar; | |||
| const var categoryVar = apsVar.getProperty ("category", {}); | |||
| const var soundVar = apsVar.getProperty ("sound", {}); | |||
| const var badgeVar = apsVar.getProperty ("badge", {}); | |||
| const var threadIdVar = apsVar.getProperty ("thread-id", {}); | |||
| PushNotifications::Notification notification; | |||
| notification.title = titleVar .toString(); | |||
| notification.body = bodyVar .toString(); | |||
| notification.groupId = threadIdVar.toString(); | |||
| notification.category = categoryVar.toString(); | |||
| notification.soundToPlay = URL (soundVar.toString()); | |||
| notification.badgeNumber = (int) badgeVar; | |||
| notification.properties = getNotificationPropertiesFromDictionaryVar (dictionaryVar); | |||
| return notification; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| struct PushNotificationsDelegate | |||
| { | |||
| PushNotificationsDelegate() : delegate ([getClass().createInstance() init]) | |||
| { | |||
| Class::setThis (delegate, this); | |||
| id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate]; | |||
| SEL selector = NSSelectorFromString (@"setPushNotificationsDelegate:"); | |||
| if ([appDelegate respondsToSelector: selector]) | |||
| [appDelegate performSelector: selector withObject: delegate]; | |||
| [NSUserNotificationCenter defaultUserNotificationCenter].delegate = delegate; | |||
| } | |||
| virtual ~PushNotificationsDelegate() | |||
| { | |||
| [NSUserNotificationCenter defaultUserNotificationCenter].delegate = nil; | |||
| } | |||
| virtual void registeredForRemoteNotifications (NSData* deviceToken) = 0; | |||
| virtual void failedToRegisterForRemoteNotifications (NSError* error) = 0; | |||
| virtual void didReceiveRemoteNotification (NSDictionary* userInfo) = 0; | |||
| virtual void didDeliverNotification (NSUserNotification* notification) = 0; | |||
| virtual void didActivateNotification (NSUserNotification* notification) = 0; | |||
| virtual bool shouldPresentNotification (NSUserNotification* notification) = 0; | |||
| protected: | |||
| ScopedPointer<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>> delegate; | |||
| private: | |||
| struct Class : public ObjCClass<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>> | |||
| { | |||
| Class() : ObjCClass<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>> ("JucePushNotificationsDelegate_") | |||
| { | |||
| addIvar<PushNotificationsDelegate*> ("self"); | |||
| addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); | |||
| addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); | |||
| addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); | |||
| addMethod (@selector (userNotificationCenter:didDeliverNotification:), didDeliverNotification, "v@:@@"); | |||
| addMethod (@selector (userNotificationCenter:didActivateNotification:), didActivateNotification, "v@:@@"); | |||
| addMethod (@selector (userNotificationCenter:shouldPresentNotification:), shouldPresentNotification, "B@:@@"); | |||
| registerClass(); | |||
| } | |||
| //============================================================================== | |||
| static PushNotificationsDelegate& getThis (id self) { return *getIvar<PushNotificationsDelegate*> (self, "self"); } | |||
| static void setThis (id self, PushNotificationsDelegate* d) { object_setInstanceVariable (self, "self", d); } | |||
| //============================================================================== | |||
| static void registeredForRemoteNotifications (id self, SEL, NSApplication*, | |||
| NSData* deviceToken) { getThis (self).registeredForRemoteNotifications (deviceToken); } | |||
| static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication*, | |||
| NSError* error) { getThis (self).failedToRegisterForRemoteNotifications (error); } | |||
| static void didReceiveRemoteNotification (id self, SEL, NSApplication*, | |||
| NSDictionary* userInfo) { getThis (self).didReceiveRemoteNotification (userInfo); } | |||
| static void didDeliverNotification (id self, SEL, NSUserNotificationCenter*, | |||
| NSUserNotification* notification) { getThis (self).didDeliverNotification (notification); } | |||
| static void didActivateNotification (id self, SEL, NSUserNotificationCenter*, | |||
| NSUserNotification* notification) { getThis (self).didActivateNotification (notification); } | |||
| static bool shouldPresentNotification (id self, SEL, NSUserNotificationCenter*, | |||
| NSUserNotification* notification) { return getThis (self).shouldPresentNotification (notification); } | |||
| }; | |||
| //============================================================================== | |||
| static Class& getClass() | |||
| { | |||
| static Class c; | |||
| return c; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| bool PushNotifications::Notification::isValid() const noexcept { return true; } | |||
| //============================================================================== | |||
| struct PushNotifications::Pimpl : private PushNotificationsDelegate | |||
| { | |||
| Pimpl (PushNotifications& p) | |||
| : owner (p) | |||
| { | |||
| } | |||
| void requestPermissionsWithSettings (const PushNotifications::Settings& settingsToUse) | |||
| { | |||
| if (isEarlierThanLion) | |||
| return; | |||
| settings = settingsToUse; | |||
| NSRemoteNotificationType types = NSUInteger ((bool) settings.allowBadge); | |||
| if (isAtLeastMountainLion) | |||
| types |= ((bool) settings.allowSound << 1 | (bool) settings.allowAlert << 2); | |||
| [[NSApplication sharedApplication] registerForRemoteNotificationTypes: types]; | |||
| } | |||
| void requestSettingsUsed() | |||
| { | |||
| if (isEarlierThanLion) | |||
| { | |||
| // no settings available | |||
| owner.listeners.call (&PushNotifications::Listener::notificationSettingsReceived, {}); | |||
| return; | |||
| } | |||
| settings.allowBadge = [NSApplication sharedApplication].enabledRemoteNotificationTypes & NSRemoteNotificationTypeBadge; | |||
| if (isAtLeastMountainLion) | |||
| { | |||
| settings.allowSound = [NSApplication sharedApplication].enabledRemoteNotificationTypes & NSRemoteNotificationTypeSound; | |||
| settings.allowAlert = [NSApplication sharedApplication].enabledRemoteNotificationTypes & NSRemoteNotificationTypeAlert; | |||
| } | |||
| owner.listeners.call (&PushNotifications::Listener::notificationSettingsReceived, settings); | |||
| } | |||
| bool areNotificationsEnabled() const { return true; } | |||
| void sendLocalNotification (const Notification& n) | |||
| { | |||
| auto* notification = PushNotificationsDelegateDetailsOsx::juceNotificationToNSUserNotification (n, isEarlierThanMavericks, isEarlierThanYosemite); | |||
| [[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification: notification]; | |||
| } | |||
| void getDeliveredNotifications() const | |||
| { | |||
| Array<PushNotifications::Notification> notifs; | |||
| for (NSUserNotification* n in [NSUserNotificationCenter defaultUserNotificationCenter].deliveredNotifications) | |||
| notifs.add (PushNotificationsDelegateDetailsOsx::nsUserNotificationToJuceNotification (n, isEarlierThanMavericks, isEarlierThanYosemite)); | |||
| owner.listeners.call (&Listener::deliveredNotificationsListReceived, notifs); | |||
| } | |||
| void removeAllDeliveredNotifications() | |||
| { | |||
| [[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications]; | |||
| } | |||
| void removeDeliveredNotification (const String& identifier) | |||
| { | |||
| PushNotifications::Notification n; | |||
| n.identifier = identifier; | |||
| auto nsNotification = PushNotificationsDelegateDetailsOsx::juceNotificationToNSUserNotification (n, isEarlierThanMavericks, isEarlierThanYosemite); | |||
| [[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification: nsNotification]; | |||
| } | |||
| void setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels) | |||
| { | |||
| ignoreUnused (groups, channels); | |||
| } | |||
| void getPendingLocalNotifications() const | |||
| { | |||
| Array<PushNotifications::Notification> notifs; | |||
| for (NSUserNotification* n in [NSUserNotificationCenter defaultUserNotificationCenter].scheduledNotifications) | |||
| notifs.add (PushNotificationsDelegateDetailsOsx::nsUserNotificationToJuceNotification (n, isEarlierThanMavericks, isEarlierThanYosemite)); | |||
| owner.listeners.call (&PushNotifications::Listener::pendingLocalNotificationsListReceived, notifs); | |||
| } | |||
| void removePendingLocalNotification (const String& identifier) | |||
| { | |||
| PushNotifications::Notification n; | |||
| n.identifier = identifier; | |||
| auto nsNotification = PushNotificationsDelegateDetailsOsx::juceNotificationToNSUserNotification (n, isEarlierThanMavericks, isEarlierThanYosemite); | |||
| [[NSUserNotificationCenter defaultUserNotificationCenter] removeScheduledNotification: nsNotification]; | |||
| } | |||
| void removeAllPendingLocalNotifications() | |||
| { | |||
| for (NSUserNotification* n in [NSUserNotificationCenter defaultUserNotificationCenter].scheduledNotifications) | |||
| [[NSUserNotificationCenter defaultUserNotificationCenter] removeScheduledNotification: n]; | |||
| } | |||
| String getDeviceToken() | |||
| { | |||
| // You need to call requestPermissionsWithSettings() first. | |||
| jassert (initialised); | |||
| return deviceToken; | |||
| } | |||
| //============================================================================== | |||
| //PushNotificationsDelegate | |||
| void registeredForRemoteNotifications (NSData* deviceTokenToUse) override | |||
| { | |||
| auto* deviceTokenString = [[[[deviceTokenToUse description] | |||
| stringByReplacingOccurrencesOfString: nsStringLiteral ("<") withString: nsStringLiteral ("")] | |||
| stringByReplacingOccurrencesOfString: nsStringLiteral (">") withString: nsStringLiteral ("")] | |||
| stringByReplacingOccurrencesOfString: nsStringLiteral (" ") withString: nsStringLiteral ("")]; | |||
| deviceToken = nsStringToJuce (deviceTokenString); | |||
| initialised = true; | |||
| owner.listeners.call (&PushNotifications::Listener::deviceTokenRefreshed, deviceToken); | |||
| } | |||
| void failedToRegisterForRemoteNotifications (NSError* error) override | |||
| { | |||
| ignoreUnused (error); | |||
| deviceToken.clear(); | |||
| } | |||
| void didReceiveRemoteNotification (NSDictionary* userInfo) override | |||
| { | |||
| auto n = PushNotificationsDelegateDetailsOsx::nsDictionaryToJuceNotification (userInfo); | |||
| owner.listeners.call (&PushNotifications::Listener::handleNotification, true, n); | |||
| } | |||
| void didDeliverNotification (NSUserNotification* notification) override | |||
| { | |||
| ignoreUnused (notification); | |||
| } | |||
| void didActivateNotification (NSUserNotification* notification) override | |||
| { | |||
| auto n = PushNotificationsDelegateDetailsOsx::nsUserNotificationToJuceNotification (notification, isEarlierThanMavericks, isEarlierThanYosemite); | |||
| if (notification.activationType == NSUserNotificationActivationTypeContentsClicked) | |||
| { | |||
| owner.listeners.call (&PushNotifications::Listener::handleNotification, notification.remote, n); | |||
| } | |||
| else | |||
| { | |||
| auto actionIdentifier = (! isEarlierThanYosemite && notification.additionalActivationAction != nil) | |||
| ? nsStringToJuce (notification.additionalActivationAction.identifier) | |||
| : nsStringToJuce (notification.actionButtonTitle); | |||
| auto reply = notification.activationType == NSUserNotificationActivationTypeReplied | |||
| ? nsStringToJuce ([notification.response string]) | |||
| : String(); | |||
| owner.listeners.call (&PushNotifications::Listener::handleNotificationAction, notification.remote, n, actionIdentifier, reply); | |||
| } | |||
| } | |||
| bool shouldPresentNotification (NSUserNotification* notification) override { return true; } | |||
| void subscribeToTopic (const String& topic) { ignoreUnused (topic); } | |||
| void unsubscribeFromTopic (const String& topic) { ignoreUnused (topic); } | |||
| void sendUpstreamMessage (const String& serverSenderId, | |||
| const String& collapseKey, | |||
| const String& messageId, | |||
| const String& messageType, | |||
| int timeToLive, | |||
| const StringPairArray& additionalData) | |||
| { | |||
| ignoreUnused (serverSenderId, collapseKey, messageId, messageType); | |||
| ignoreUnused (timeToLive, additionalData); | |||
| } | |||
| private: | |||
| PushNotifications& owner; | |||
| const bool isEarlierThanLion = std::floor (NSFoundationVersionNumber) < std::floor (NSFoundationVersionNumber10_7); | |||
| const bool isAtLeastMountainLion = std::floor (NSFoundationVersionNumber) >= NSFoundationVersionNumber10_7; | |||
| const bool isEarlierThanMavericks = std::floor (NSFoundationVersionNumber) < NSFoundationVersionNumber10_9; | |||
| const bool isEarlierThanYosemite = std::floor (NSFoundationVersionNumber) <= NSFoundationVersionNumber10_9; | |||
| bool initialised = false; | |||
| String deviceToken; | |||
| PushNotifications::Settings settings; | |||
| }; | |||
| } // namespace juce | |||