1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Accessibility: Add AccessibilityHandler::postSystemNotification() function for posting an OS-specific accessible notification

This commit is contained in:
reuk 2024-11-21 21:26:55 +00:00
parent 6d10eb536f
commit 269ebbb525
No known key found for this signature in database
11 changed files with 308 additions and 149 deletions

View file

@ -36,10 +36,50 @@ namespace juce
{
//==============================================================================
#if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC
#if ! JUCE_PUSH_NOTIFICATIONS_IMPL
struct PushNotifications::Impl
{
explicit Impl (PushNotifications& o) : owner (o) {}
void requestPermissionsWithSettings (const Settings&) const
{
owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
}
void requestSettingsUsed() const
{
owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
}
bool areNotificationsEnabled() const { return false; }
void getDeliveredNotifications() const {}
void removeAllDeliveredNotifications() const {}
String getDeviceToken() const { return {}; }
void setupChannels (const Array<ChannelGroup>&, const Array<Channel>&) const {}
void getPendingLocalNotifications() const {}
void removeAllPendingLocalNotifications() const {}
void subscribeToTopic (const String&) const {}
void unsubscribeFromTopic (const String&) const {}
void sendLocalNotification (const Notification&) const {}
void removeDeliveredNotification (const String&) const {}
void removePendingLocalNotification (const String&) const {}
void sendUpstreamMessage (const String&,
const String&,
const String&,
const String&,
int,
const StringPairArray&) const {}
private:
PushNotifications& owner;
};
bool PushNotifications::Notification::isValid() const noexcept { return true; }
#endif
//==============================================================================
PushNotifications::Notification::Notification (const Notification& other)
: identifier (other.identifier),
title (other.title),
@ -82,9 +122,7 @@ PushNotifications::Notification::Notification (const Notification& other)
//==============================================================================
PushNotifications::PushNotifications()
#if JUCE_PUSH_NOTIFICATIONS
: pimpl (new Pimpl (*this))
#endif
: pimpl (new Impl (*this))
{
}
@ -93,128 +131,90 @@ PushNotifications::~PushNotifications() { clearSingletonInstance(); }
void PushNotifications::addListener (Listener* l) { listeners.add (l); }
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); }
void PushNotifications::requestPermissionsWithSettings ([[maybe_unused]] const PushNotifications::Settings& settings)
void PushNotifications::requestPermissionsWithSettings (const Settings& settings)
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestPermissionsWithSettings (settings);
#else
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
void PushNotifications::requestSettingsUsed()
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestSettingsUsed();
#else
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
bool PushNotifications::areNotificationsEnabled() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->areNotificationsEnabled();
#else
return false;
#endif
}
void PushNotifications::getDeliveredNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getDeliveredNotifications();
#endif
}
void PushNotifications::removeAllDeliveredNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllDeliveredNotifications();
#endif
}
String PushNotifications::getDeviceToken() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->getDeviceToken();
#else
return {};
#endif
}
void PushNotifications::setupChannels ([[maybe_unused]] const Array<ChannelGroup>& groups, [[maybe_unused]] const Array<Channel>& channels)
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups,
const Array<Channel>& channels)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->setupChannels (groups, channels);
#endif
}
void PushNotifications::getPendingLocalNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getPendingLocalNotifications();
#endif
}
void PushNotifications::removeAllPendingLocalNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllPendingLocalNotifications();
#endif
}
void PushNotifications::subscribeToTopic ([[maybe_unused]] const String& topic)
void PushNotifications::subscribeToTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->subscribeToTopic (topic);
#endif
}
void PushNotifications::unsubscribeFromTopic ([[maybe_unused]] const String& topic)
void PushNotifications::unsubscribeFromTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->unsubscribeFromTopic (topic);
#endif
}
void PushNotifications::sendLocalNotification ([[maybe_unused]] const Notification& n)
void PushNotifications::sendLocalNotification (const Notification& n)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendLocalNotification (n);
#endif
}
void PushNotifications::removeDeliveredNotification ([[maybe_unused]] const String& identifier)
void PushNotifications::removeDeliveredNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeDeliveredNotification (identifier);
#endif
}
void PushNotifications::removePendingLocalNotification ([[maybe_unused]] const String& identifier)
void PushNotifications::removePendingLocalNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removePendingLocalNotification (identifier);
#endif
}
void PushNotifications::sendUpstreamMessage ([[maybe_unused]] const String& serverSenderId,
[[maybe_unused]] const String& collapseKey,
[[maybe_unused]] const String& messageId,
[[maybe_unused]] const String& messageType,
[[maybe_unused]] int timeToLive,
[[maybe_unused]] const StringPairArray& additionalData)
void PushNotifications::sendUpstreamMessage (const String& serverSenderId,
const String& collapseKey,
const String& messageId,
const String& messageType,
int timeToLive,
const StringPairArray& additionalData)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendUpstreamMessage (serverSenderId,
collapseKey,
messageId,
messageType,
timeToLive,
additionalData);
#endif
}
//==============================================================================
@ -234,4 +234,92 @@ void PushNotifications::Listener::upstreamMessageSent ([[maybe_unused]] const St
void PushNotifications::Listener::upstreamMessageSendingError ([[maybe_unused]] const String& messageId,
[[maybe_unused]] const String& error) {}
//==============================================================================
void privatePostSystemNotification (const String& notificationTitle, const String& notificationBody);
void privatePostSystemNotification ([[maybe_unused]] const String& notificationTitle,
[[maybe_unused]] const String& notificationBody)
{
#if JUCE_PUSH_NOTIFICATIONS
#if JUCE_ANDROID || JUCE_IOS || JUCE_MAC
auto* notificationsInstance = PushNotifications::getInstance();
if (notificationsInstance == nullptr)
return;
#if JUCE_ANDROID
notificationsInstance->requestPermissionsWithSettings ({});
static auto channels = std::invoke ([]() -> Array<PushNotifications::Channel>
{
PushNotifications::Channel chan;
chan.identifier = "1";
chan.name = "Notifications";
chan.description = "Accessibility notifications";
chan.groupId = "accessibility";
chan.ledColour = Colours::yellow;
chan.canShowBadge = true;
chan.enableLights = true;
chan.enableVibration = true;
chan.soundToPlay = URL ("default_os_sound");
chan.vibrationPattern = { 1000, 1000 };
return { chan };
});
notificationsInstance->setupChannels ({ PushNotifications::ChannelGroup { "accessibility", "accessibility" } },
channels);
#else
static auto settings = std::invoke ([]
{
PushNotifications::Settings s;
s.allowAlert = true;
s.allowBadge = true;
s.allowSound = true;
#if JUCE_IOS
PushNotifications::Settings::Category c;
c.identifier = "Accessibility";
s.categories = { c };
#endif
return s;
});
notificationsInstance->requestPermissionsWithSettings (settings);
#endif
const auto notification = std::invoke ([&notificationTitle, &notificationBody]
{
PushNotifications::Notification n;
n.identifier = String (Random::getSystemRandom().nextInt());
n.title = notificationTitle;
n.body = notificationBody;
#if JUCE_IOS
n.category = "Accessibility";
#elif JUCE_ANDROID
n.channelId = "1";
n.icon = "accessibilitynotificationicon";
#endif
return n;
});
if (notification.isValid())
notificationsInstance->sendLocalNotification (notification);
#else
SystemTrayIconComponent systemTrayIcon;
Image im (Image::ARGB, 128, 128, true);
systemTrayIcon.setIconImage (im, im);
systemTrayIcon.showInfoBubble (notificationTitle, notificationBody);
#endif
#endif
}
} // namespace juce

View file

@ -403,8 +403,8 @@ public:
*/
struct Category
{
juce::String identifier; /**< unique identifier */
juce::Array<Action> actions; /**< optional list of actions within this category */
String identifier; /**< unique identifier */
Array<Action> actions; /**< optional list of actions within this category */
bool sendDismissAction = false; /**< whether dismiss action will be sent to the app */
};
@ -703,12 +703,8 @@ private:
friend struct JuceFirebaseMessagingService;
#endif
#if JUCE_PUSH_NOTIFICATIONS
struct Pimpl;
friend struct Pimpl;
std::unique_ptr<Pimpl> pimpl;
#endif
struct Impl;
std::unique_ptr<Impl> pimpl;
};
} // namespace juce

View file

@ -35,6 +35,8 @@
namespace juce
{
#define JUCE_PUSH_NOTIFICATIONS_IMPL 1
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
METHOD (enableLights, "enableLights", "(Z)V") \
@ -274,9 +276,9 @@ bool PushNotifications::Notification::isValid() const noexcept
}
//==============================================================================
struct PushNotifications::Pimpl
struct PushNotifications::Impl
{
explicit Pimpl (PushNotifications& p)
explicit Impl (PushNotifications& p)
: owner (p)
{}
@ -306,7 +308,7 @@ struct PushNotifications::Pimpl
const auto notifyListeners = []
{
if (auto* instance = PushNotifications::getInstance())
instance->listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
instance->listeners.call ([] (Listener& l) { l.notificationSettingsReceived (makeDefaultSettings()); });
};
if (MessageManager::getInstance()->isThisTheMessageThread())
@ -318,7 +320,7 @@ struct PushNotifications::Pimpl
void requestSettingsUsed()
{
owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived (makeDefaultSettings()); });
}
void sendLocalNotification (const Notification& n)
@ -1573,6 +1575,15 @@ struct PushNotifications::Pimpl
&& env->CallBooleanMethod (extras, AndroidBundle.containsKey, javaString ("google.message_id").get());
}
static Settings makeDefaultSettings()
{
Settings settings;
settings.allowAlert = true;
settings.allowBadge = true;
settings.allowSound = true;
return settings;
}
PushNotifications& owner;
};
@ -1640,14 +1651,14 @@ bool juce_handleNotificationIntent (void* intent)
{
auto* instance = PushNotifications::getInstanceWithoutCreating();
if (PushNotifications::Pimpl::isDeleteNotificationIntent ((jobject) intent))
if (PushNotifications::Impl::isDeleteNotificationIntent ((jobject) intent))
{
if (instance)
instance->pimpl->notifyListenersAboutLocalNotificationDeleted (LocalRef<jobject> ((jobject) intent));
return true;
}
else if (PushNotifications::Pimpl::isLocalNotificationIntent ((jobject) intent))
else if (PushNotifications::Impl::isLocalNotificationIntent ((jobject) intent))
{
if (instance)
instance->pimpl->notifyListenersAboutLocalNotification (LocalRef<jobject> ((jobject) intent));
@ -1655,7 +1666,7 @@ bool juce_handleNotificationIntent (void* intent)
return true;
}
#if defined (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
else if (PushNotifications::Pimpl::isRemoteNotificationIntent ((jobject) intent))
else if (PushNotifications::Impl::isRemoteNotificationIntent ((jobject) intent))
{
if (instance)
instance->pimpl->notifyListenersAboutRemoteNotificationFromSystemTray (LocalRef<jobject> ((jobject) intent));

View file

@ -35,6 +35,8 @@
namespace juce
{
#define JUCE_PUSH_NOTIFICATIONS_IMPL 1
struct PushNotificationsDelegateDetails
{
//==============================================================================
@ -301,9 +303,9 @@ bool PushNotifications::Notification::isValid() const noexcept
}
//==============================================================================
struct PushNotifications::Pimpl
struct PushNotifications::Impl
{
Pimpl (PushNotifications& p)
explicit Impl (PushNotifications& p)
: owner (p)
{
Class::setThis (delegate.get(), this);
@ -555,7 +557,7 @@ private:
Class()
: ObjCClass ("JucePushNotificationsDelegate_")
{
addIvar<Pimpl*> ("self");
addIvar<Impl*> ("self");
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (id self, SEL, UIApplication*, NSData* data)
{
@ -596,8 +598,8 @@ private:
}
//==============================================================================
static Pimpl& getThis (id self) { return *getIvar<Pimpl*> (self, "self"); }
static void setThis (id self, Pimpl* d) { object_setInstanceVariable (self, "self", d); }
static Impl& getThis (id self) { return *getIvar<Impl*> (self, "self"); }
static void setThis (id self, Impl* d) { object_setInstanceVariable (self, "self", d); }
};
//==============================================================================

View file

@ -35,6 +35,8 @@
namespace juce
{
#define JUCE_PUSH_NOTIFICATIONS_IMPL 1
JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS
namespace PushNotificationsDelegateDetailsOsx
@ -343,9 +345,9 @@ private:
bool PushNotifications::Notification::isValid() const noexcept { return true; }
//==============================================================================
struct PushNotifications::Pimpl : private PushNotificationsDelegate
struct PushNotifications::Impl : private PushNotificationsDelegate
{
Pimpl (PushNotifications& p)
explicit Impl (PushNotifications& p)
: owner (p)
{
}