// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FIREBASE_MESSAGING_CLIENT_CPP_INCLUDE_FIREBASE_MESSAGING_H_ #define FIREBASE_MESSAGING_CLIENT_CPP_INCLUDE_FIREBASE_MESSAGING_H_ #include #include #include #include #include "firebase/app.h" #include "firebase/future.h" #include "firebase/internal/common.h" #if !defined(DOXYGEN) && !defined(SWIG) FIREBASE_APP_REGISTER_CALLBACKS_REFERENCE(messaging) #endif // !defined(DOXYGEN) && !defined(SWIG) namespace firebase { /// @brief Firebase Cloud Messaging API. /// /// Firebase Cloud Messaging allows you to send data from your server to your /// users' devices, and receive messages from devices on the same connection /// if you're using a XMPP server. /// /// The FCM service handles all aspects of queueing of messages and delivery /// to client applications running on target devices. namespace messaging { /// @brief A class to configure the behavior of Firebase Cloud Messaging. /// /// This class contains various configuration options that control some of /// Firebase Cloud Messaging's behavior. struct MessagingOptions { /// Default constructor. MessagingOptions() : suppress_notification_permission_prompt(false) {} /// If true, do not display the prompt to the user requesting permission to /// allow notifications to this app. If the prompt is suppressed in this way, /// the developer must manually prompt the user for permission at some point /// in the future using `RequestPermission()`. /// /// If this prompt has already been accepted once in the past the prompt will /// not be displayed again. /// /// This option currently only applies to iOS. bool suppress_notification_permission_prompt; }; /// @brief Data structure for parameters that are unique to the Android /// implementation. struct AndroidNotificationParams { /// The channel id that was provided when the message was sent. std::string channel_id; }; /// Used for messages that display a notification. /// /// On android, this requires that the app is using the Play Services client /// library. struct Notification { Notification() : android(nullptr) {} /// Copy constructor. Makes a deep copy of this Message. Notification(const Notification& other) : android(nullptr) { *this = other; } /// Copy assignment operator. Makes a deep copy of this Message. Notification& operator=(const Notification& other) { this->title = other.title; this->body = other.body; this->icon = other.icon; this->sound = other.sound; this->tag = other.tag; this->color = other.color; this->click_action = other.click_action; this->body_loc_key = other.body_loc_key; this->body_loc_args = other.body_loc_args; this->title_loc_key = other.title_loc_key; this->title_loc_args = other.title_loc_args; delete this->android; if (other.android) { this->android = new AndroidNotificationParams(*other.android); } else { this->android = nullptr; } return *this; } /// Destructor. ~Notification() { delete android; } /// Indicates notification title. This field is not visible on iOS phones /// and tablets. std::string title; /// Indicates notification body text. std::string body; /// Indicates notification icon. Sets value to myicon for drawable resource /// myicon. std::string icon; /// Indicates a sound to play when the device receives the notification. /// Supports default, or the filename of a sound resource bundled in the /// app. /// /// Android sound files must reside in /res/raw/, while iOS sound files /// can be in the main bundle of the client app or in the Library/Sounds /// folder of the app’s data container. std::string sound; /// Indicates the badge on the client app home icon. iOS only. std::string badge; /// Indicates whether each notification results in a new entry in the /// notification drawer on Android. If not set, each request creates a new /// notification. If set, and a notification with the same tag is already /// being shown, the new notification replaces the existing one in the /// notification drawer. std::string tag; /// Indicates color of the icon, expressed in \#rrggbb format. Android only. std::string color; /// The action associated with a user click on the notification. /// /// On Android, if this is set, an activity with a matching intent filter is /// launched when user clicks the notification. /// /// If set on iOS, corresponds to category in APNS payload. std::string click_action; /// Indicates the key to the body string for localization. /// /// On iOS, this corresponds to "loc-key" in APNS payload. /// /// On Android, use the key in the app's string resources when populating this /// value. std::string body_loc_key; /// Indicates the string value to replace format specifiers in body string /// for localization. /// /// On iOS, this corresponds to "loc-args" in APNS payload. /// /// On Android, these are the format arguments for the string resource. For /// more information, see [Formatting strings][1]. /// /// [1]: /// https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling std::vector body_loc_args; /// Indicates the key to the title string for localization. /// /// On iOS, this corresponds to "title-loc-key" in APNS payload. /// /// On Android, use the key in the app's string resources when populating this /// value. std::string title_loc_key; /// Indicates the string value to replace format specifiers in title string /// for localization. /// /// On iOS, this corresponds to "title-loc-args" in APNS payload. /// /// On Android, these are the format arguments for the string resource. For /// more information, see [Formatting strings][1]. /// /// [1]: /// https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling std::vector title_loc_args; /// Parameters that are unique to the Android implementation. AndroidNotificationParams* android; }; /// @brief Data structure used to send messages to, and receive messages from, /// cloud messaging. struct Message { /// Initialize the message. Message() : time_to_live(0), notification(nullptr), notification_opened(false), sent_time(0) {} /// Destructor. ~Message() { delete notification; } /// Copy constructor. Makes a deep copy of this Message. Message(const Message& other) : notification(nullptr) { *this = other; } /// Copy assignment operator. Makes a deep copy of this Message. Message& operator=(const Message& other) { this->from = other.from; this->to = other.to; this->collapse_key = other.collapse_key; this->data = other.data; this->raw_data = other.raw_data; this->message_id = other.message_id; this->message_type = other.message_type; this->priority = other.priority; this->original_priority = other.original_priority; this->sent_time = other.sent_time; this->time_to_live = other.time_to_live; this->error = other.error; this->error_description = other.error_description; delete this->notification; if (other.notification) { this->notification = new Notification(*other.notification); } else { this->notification = nullptr; } this->notification_opened = other.notification_opened; this->link = other.link; return *this; } /// Authenticated ID of the sender. This is a project number in most cases. /// /// Any value starting with google.com, goog. or gcm. are reserved. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string from; /// This parameter specifies the recipient of a message. /// /// For example it can be a registration token, a topic name, an Instance ID /// or project ID. /// /// For upstream messages use the format PROJECT_ID@gcm.googleapis.com. /// /// This field is used for both upstream messages sent with /// firebase::messaging::Send() and downstream messages received through /// Listener::OnMessage(). For upstream messages, /// PROJECT_ID@gcm.googleapis.com or Instance ID are accepted. std::string to; /// This parameter identifies a group of messages (e.g., with collapse_key: /// "Updates Available") that can be collapsed, so that only the last message /// gets sent when delivery can be resumed. This is intended to avoid sending /// too many of the same messages when the device comes back online or becomes /// active. /// /// Note that there is no guarantee of the order in which messages get sent. /// /// Note: A maximum of 4 different collapse keys is allowed at any given time. /// This means a FCM connection server can simultaneously store 4 different /// send-to-sync messages per client app. If you exceed this number, there is /// no guarantee which 4 collapse keys the FCM connection server will keep. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string collapse_key; /// The metadata, including all original key/value pairs. Includes some of the /// HTTP headers used when sending the message. `gcm`, `google` and `goog` /// prefixes are reserved for internal use. /// /// This field is used for both upstream messages sent with /// firebase::messaging::Send() and downstream messages received through /// Listener::OnMessage(). std::map data; /// Binary payload. This field is currently unused. std::string raw_data; /// Message ID. This can be specified by sender. Internally a hash of the /// message ID and other elements will be used for storage. The ID must be /// unique for each topic subscription - using the same ID may result in /// overriding the original message or duplicate delivery. /// /// This field is used for both upstream messages sent with /// firebase::messaging::Send() and downstream messages received through /// Listener::OnMessage(). std::string message_id; /// Equivalent with a content-type. /// /// Defined values: /// - "deleted_messages" - indicates the server had too many messages and /// dropped some, and the client should sync with his own server. /// Current limit is 100 messages stored. /// - "send_event" - indicates an upstream message has been pushed to the /// FCM server. It does not guarantee the upstream destination received /// it. /// Parameters: "message_id" /// - "send_error" - indicates an upstream message expired, without being /// sent to the FCM server. /// Parameters: "message_id" and "error" /// /// If this field is missing, the message is a regular message. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string message_type; /// Sets the priority of the message. Valid values are "normal" and "high." On /// iOS, these correspond to APNs priority 5 and 10. /// /// By default, messages are sent with normal priority. Normal priority /// optimizes the client app's battery consumption, and should be used unless /// immediate delivery is required. For messages with normal priority, the app /// may receive the message with unspecified delay. /// /// When a message is sent with high priority, it is sent immediately, and the /// app can wake a sleeping device and open a network connection to your /// server. /// /// For more information, see [Setting the priority of a message][1]. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). /// /// [1]: /// https://firebase.google.com/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message std::string priority; /// This parameter specifies how long (in seconds) the message should be kept /// in FCM storage if the device is offline. The maximum time to live /// supported is 4 weeks, and the default value is 4 weeks. For more /// information, see [Setting the lifespan of a message][1]. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). /// /// [1]: https://firebase.google.com/docs/cloud-messaging/concept-options#ttl int32_t time_to_live; /// Error code. Used in "nack" messages for CCS, and in responses from the /// server. /// See the CCS specification for the externally-supported list. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string error; /// Human readable details about the error. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string error_description; /// Optional notification to show. This only set if a notification was /// received with this message, otherwise it is null. /// /// The notification is only guaranteed to be valid during the call to /// Listener::OnMessage(). If you need to keep it around longer you will need /// to make a copy of either the Message or Notification. Copying the Message /// object implicitly makes a deep copy of the notification (allocated with /// new) which is owned by the Message. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). Notification* notification; /// A flag indicating whether this message was opened by tapping a /// notification in the OS system tray. If the message was received this way /// this flag is set to true. bool notification_opened; /// The link into the app from the message. /// /// This field is only used for downstream messages received through /// Listener::OnMessage(). std::string link; /// @cond FIREBASE_APP_INTERNAL /// Original priority of the message. std::string original_priority; /// UTC timestamp in milliseconds when the message was sent. /// See https://en.wikipedia.org/wiki/Unix_time for details of UTC. int64_t sent_time; /// @endcond }; /// @brief Base class used to receive messages from Firebase Cloud Messaging. /// /// You need to override base class methods to handle any events required by the /// application. Methods are invoked asynchronously and may be invoked on other /// threads. class Listener { public: virtual ~Listener(); /// Called on the client when a message arrives. /// /// @param[in] message The data describing this message. virtual void OnMessage(const Message& message) = 0; /// Called on the client when a registration token arrives. This function /// will eventually be called in response to a call to /// firebase::messaging::Initialize(...). /// /// @param[in] token The registration token. virtual void OnTokenReceived(const char* token) = 0; }; /// @brief Initialize Firebase Cloud Messaging. /// /// After Initialize is called, the implementation may call functions on the /// Listener provided at any time. /// /// @param[in] app The Firebase App object for this application. /// @param[in] listener A Listener object that listens for events from the /// Firebase Cloud Messaging servers. /// /// @return kInitResultSuccess if initialization succeeded, or /// kInitResultFailedMissingDependency on Android if Google Play services is /// not available on the current device. InitResult Initialize(const App& app, Listener* listener); /// @brief Initialize Firebase Cloud Messaging. /// /// After Initialize is called, the implementation may call functions on the /// Listener provided at any time. /// /// @param[in] app The Firebase App object for this application. /// @param[in] listener A Listener object that listens for events from the /// Firebase Cloud Messaging servers. /// @param[in] options A set of options that configure the /// initialzation behavior of Firebase Cloud Messaging. /// /// @return kInitResultSuccess if initialization succeeded, or /// kInitResultFailedMissingDependency on Android if Google Play services is /// not available on the current device. InitResult Initialize(const App& app, Listener* listener, const MessagingOptions& options); /// @brief Terminate Firebase Cloud Messaging. /// /// Frees resources associated with Firebase Cloud Messaging. /// /// @note On Android, the services will not be shut down by this method. void Terminate(); /// Determines if automatic token registration during initalization is enabled. /// /// @return true if auto token registration is enabled and false if disabled. bool IsTokenRegistrationOnInitEnabled(); /// Enable or disable token registration during initialization of Firebase Cloud /// Messaging. /// /// This token is what identifies the user to Firebase, so disabling this avoids /// creating any new identity and automatically sending it to Firebase, unless /// consent has been granted. /// /// If this setting is enabled, it triggers the token registration refresh /// immediately. This setting is persisted across app restarts and overrides the /// setting "firebase_messaging_auto_init_enabled" specified in your Android /// manifest (on Android) or Info.plist (on iOS). /// ///

By default, token registration during initialization is enabled. /// /// The registration happens before you can programmatically disable it, so /// if you need to change the default, (for example, because you want to prompt /// the user before FCM generates/refreshes a registration token on app /// startup), add to your application’s manifest: /// /// /// @if NOT_DOXYGEN /// /// @else /// @code /// <meta-data android:name="firebase_messaging_auto_init_enabled" /// android:value="false" /> /// @endcode /// @endif /// /// or on iOS to your Info.plist: /// /// @if NOT_DOXYGEN /// FirebaseMessagingAutoInitEnabled /// /// @else /// @code /// <key>FirebaseMessagingAutoInitEnabled</key> /// <false/> /// @endcode /// @endif /// /// @param enable sets if a registration token should be requested on /// initialization. void SetTokenRegistrationOnInitEnabled(bool enable); /// @brief Set the listener for events from the Firebase Cloud Messaging /// servers. /// /// A listener must be set for the application to receive messages from /// the Firebase Cloud Messaging servers. The implementation may call functions /// on the Listener provided at any time. /// /// @param[in] listener A Listener object that listens for events from the /// Firebase Cloud Messaging servers. /// /// @return Pointer to the previously set listener. Listener* SetListener(Listener* listener); /// Error code returned by Firebase Cloud Messaging C++ functions. enum Error { /// The operation was a success, no error occurred. kErrorNone = 0, /// Permission to receive notifications was not granted. kErrorFailedToRegisterForRemoteNotifications, /// Topic name is invalid for subscription/unsubscription. kErrorInvalidTopicName, /// Could not subscribe/unsubscribe because there is no registration token. kErrorNoRegistrationToken, /// Unknown error. kErrorUnknown, }; /// @brief Displays a prompt to the user requesting permission to display /// notifications. /// /// The permission prompt only appears on iOS. If the user has already agreed to /// allow notifications, no prompt is displayed and the returned future is /// completed immediately. /// /// @return A future that completes when the notification prompt has been /// dismissed. Future RequestPermission(); /// @brief Gets the result of the most recent call to RequestPermission(); /// /// @returns Result of the most recent call to RequestPermission(). Future RequestPermissionLastResult(); /// Send an upstream ("device to cloud") message. You can only use the upstream /// feature if your FCM implementation uses the XMPP-based Cloud Connection /// Server. The current limits for max storage time and number of outstanding /// messages per application are documented in the [FCM Developers Guide]. /// /// [FCM Developers Guide]: https://firebase.google.com/docs/cloud-messaging/ /// /// @param[in] message The message to send upstream. void Send(const Message& message); /// @brief Subscribe to receive all messages to the specified topic. /// /// Subscribes an app instance to a topic, enabling it to receive messages /// sent to that topic. /// /// Call this function from the main thread. FCM is not thread safe. /// /// @param[in] topic The name of the topic to subscribe. Must match the /// following regular expression: `[a-zA-Z0-9-_.~%]{1,900}`. Future Subscribe(const char* topic); /// @brief Gets the result of the most recent call to Unsubscribe(); /// /// @returns Result of the most recent call to Unsubscribe(). Future SubscribeLastResult(); /// @brief Unsubscribe from a topic. /// /// Unsubscribes an app instance from a topic, stopping it from receiving /// any further messages sent to that topic. /// /// Call this function from the main thread. FCM is not thread safe. /// /// @param[in] topic The name of the topic to unsubscribe from. Must match the /// following regular expression: `[a-zA-Z0-9-_.~%]{1,900}`. Future Unsubscribe(const char* topic); /// @brief Gets the result of the most recent call to Unsubscribe(); /// /// @returns Result of the most recent call to Unsubscribe(). Future UnsubscribeLastResult(); class PollableListenerImpl; /// @brief A listener that can be polled to consume pending `Message`s. /// /// This class is intended to be used with applications that have a main loop /// that frequently updates, such as in the case of a game that has a main /// loop that updates 30 to 60 times a second. Rather than respond to incoming /// messages and tokens via the `OnMessage` virtual function, this class will /// queue up the message internally in a thread-safe manner so that it can be /// consumed with `PollMessage`. For example: /// /// ::firebase::messaging::PollableListener listener; /// ::firebase::messaging::Initialize(app, &listener); /// /// while (true) { /// std::string token; /// if (listener.PollRegistrationToken(&token)) { /// LogMessage("Received a registration token"); /// } /// /// ::firebase::messaging::Message message; /// while (listener.PollMessage(&message)) { /// LogMessage("Received a new message"); /// } /// /// // Remainder of application logic... /// } class PollableListener : public Listener { public: /// @brief The default constructor. PollableListener(); /// @brief The required virtual destructor. virtual ~PollableListener(); /// @brief An implementation of `OnMessage` which adds the incoming messages /// to a queue, which can be consumed by calling `PollMessage`. virtual void OnMessage(const Message& message); /// @brief An implementation of `OnTokenReceived` which stores the incoming /// token so that it can be consumed by calling `PollRegistrationToken`. virtual void OnTokenReceived(const char* token); /// @brief Returns the first message queued up, if any. /// /// If one or more messages has been received, the first message in the /// queue will be popped and used to populate the `message` argument and the /// function will return `true`. If there are no pending messages, `false` is /// returned. This function should be called in a loop until all messages have /// been consumed, like so: /// /// ::firebase::messaging::Message message; /// while (listener.PollMessage(&message)) { /// LogMessage("Received a new message"); /// } /// /// @param[out] message The `Message` struct to be populated. If there were no /// pending messages, `message` is not modified. /// /// @return Returns `true` if there was a pending message, `false` otherwise. bool PollMessage(Message* message); /// @brief Returns the registration key, if a new one has been received. /// /// When a new registration token is received, it is cached internally and can /// be retrieved by calling `PollRegistrationToken`. The cached registration /// token will be used to populate the `token` argument, then the cache will /// be cleared and the function will return `true`. If there is no cached /// registration token this function retuns `false`. /// /// std::string token; /// if (listener.PollRegistrationToken(&token)) { /// LogMessage("Received a registration token"); /// } /// /// @param[out] token A string to be populated with the new token if one has /// been received. If there were no new token, the string is left unmodified. /// /// @return Returns `true` if there was a new token, `false` otherwise. bool PollRegistrationToken(std::string* token) { bool got_token; std::string token_received = PollRegistrationToken(&got_token); if (got_token) { *token = token_received; } return got_token; } private: std::string PollRegistrationToken(bool* got_token); // The implementation of the `PollableListener`. PollableListenerImpl* impl_; }; } // namespace messaging } // namespace firebase #endif // FIREBASE_MESSAGING_CLIENT_CPP_SRC_INCLUDE_FIREBASE_MESSAGING_H_