2020import static com .google .common .base .Preconditions .checkNotNull ;
2121
2222import com .google .api .core .ApiFuture ;
23+ import com .google .api .core .ApiFutures ;
2324import com .google .common .annotations .VisibleForTesting ;
2425import com .google .common .base .Strings ;
2526import com .google .common .base .Supplier ;
2627import com .google .common .base .Suppliers ;
2728import com .google .common .collect .ImmutableList ;
29+ import com .google .firebase .ErrorCode ;
2830import com .google .firebase .FirebaseApp ;
2931import com .google .firebase .ImplFirebaseTrampolines ;
3032import com .google .firebase .internal .CallableOperation ;
3133import com .google .firebase .internal .FirebaseService ;
3234import com .google .firebase .internal .NonNull ;
3335
36+ import java .util .ArrayList ;
3437import java .util .List ;
38+ import java .util .concurrent .ExecutionException ;
3539
3640/**
3741 * This class is the entry point for all server-side Firebase Cloud Messaging actions.
@@ -91,7 +95,7 @@ public String send(@NonNull Message message) throws FirebaseMessagingException {
9195 *
9296 * <p>If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
9397 * FCM performs all the necessary validations and emulates the send operation. The {@code dryRun}
94- * option is useful for determining whether an FCM registration has been deleted. However, it
98+ * option is useful for determining whether an FCM registration has been deleted. However, it
9599 * cannot be used to validate APNs tokens.
96100 *
97101 * @param message A non-null {@link Message} to be sent.
@@ -139,6 +143,191 @@ protected String execute() throws FirebaseMessagingException {
139143 };
140144 }
141145
146+ /**
147+ * Sends each message in the given list via Firebase Cloud Messaging.
148+ * Unlike {@link #sendAll(List)}, this method makes a single HTTP call for each message in the
149+ * given array.
150+ *
151+ * <p>The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
152+ * value corresponds to the order of input messages.
153+ *
154+ * @param messages A non-null, non-empty list containing up to 500 messages.
155+ * @return A {@link BatchResponse} indicating the result of the operation.
156+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
157+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
158+ * failure -- i.e. none of the messages in the list could be sent. Partial failures or no
159+ * failures are only indicated by a {@link BatchResponse}.
160+ */
161+ public BatchResponse sendEach (@ NonNull List <Message > messages ) throws FirebaseMessagingException {
162+ return sendEachOp (messages , false ).call ();
163+ }
164+
165+
166+ /**
167+ * Sends each message in the given list via Firebase Cloud Messaging.
168+ * Unlike {@link #sendAll(List)}, this method makes a single HTTP call for each message in the
169+ * given array.
170+ *
171+ * <p>If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
172+ * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
173+ * option is useful for determining whether an FCM registration has been deleted. But it cannot be
174+ * used to validate APNs tokens.
175+ *
176+ * <p>The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
177+ * value corresponds to the order of input messages.
178+ *
179+ * @param messages A non-null, non-empty list containing up to 500 messages.
180+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
181+ * @return A {@link BatchResponse} indicating the result of the operation.
182+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
183+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
184+ * failure -- i.e. none of the messages in the list could be sent. Partial failures or no
185+ * failures are only indicated by a {@link BatchResponse}.
186+ */
187+ public BatchResponse sendEach (
188+ @ NonNull List <Message > messages , boolean dryRun ) throws FirebaseMessagingException {
189+ return sendEachOp (messages , dryRun ).call ();
190+ }
191+
192+ /**
193+ * Similar to {@link #sendEach(List)} but performs the operation asynchronously.
194+ *
195+ * @param messages A non-null, non-empty list containing up to 500 messages.
196+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
197+ * the messages have been sent.
198+ */
199+ public ApiFuture <BatchResponse > sendEachAsync (@ NonNull List <Message > messages ) {
200+ return sendEachOp (messages , false ).callAsync (app );
201+ }
202+
203+ /**
204+ * Similar to {@link #sendEach(List, boolean)} but performs the operation asynchronously.
205+ *
206+ * @param messages A non-null, non-empty list containing up to 500 messages.
207+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
208+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
209+ * the messages have been sent.
210+ */
211+ public ApiFuture <BatchResponse > sendEachAsync (@ NonNull List <Message > messages , boolean dryRun ) {
212+ return sendEachOp (messages , dryRun ).callAsync (app );
213+ }
214+
215+ private CallableOperation <BatchResponse , FirebaseMessagingException > sendEachOp (
216+ final List <Message > messages , final boolean dryRun ) {
217+ final List <Message > immutableMessages = ImmutableList .copyOf (messages );
218+ checkArgument (!immutableMessages .isEmpty (), "messages list must not be empty" );
219+ checkArgument (immutableMessages .size () <= 500 ,
220+ "messages list must not contain more than 500 elements" );
221+
222+ return new CallableOperation <BatchResponse , FirebaseMessagingException >() {
223+ @ Override
224+ protected BatchResponse execute () throws FirebaseMessagingException {
225+ List <ApiFuture <SendResponse >> list = new ArrayList <>();
226+ for (Message message : immutableMessages ) {
227+ ApiFuture <SendResponse > messageId = sendOpForSendResponse (message , dryRun ).callAsync (app );
228+ list .add (messageId );
229+ }
230+ try {
231+ List <SendResponse > responses = ApiFutures .allAsList (list ).get ();
232+ return new BatchResponseImpl (responses );
233+ } catch (InterruptedException | ExecutionException e ) {
234+ throw new FirebaseMessagingException (ErrorCode .CANCELLED , SERVICE_ID );
235+ }
236+ }
237+ };
238+ }
239+
240+ private CallableOperation <SendResponse , FirebaseMessagingException > sendOpForSendResponse (
241+ final Message message , final boolean dryRun ) {
242+ checkNotNull (message , "message must not be null" );
243+ final FirebaseMessagingClient messagingClient = getMessagingClient ();
244+ return new CallableOperation <SendResponse , FirebaseMessagingException >() {
245+ @ Override
246+ protected SendResponse execute () {
247+ try {
248+ String messageId = messagingClient .send (message , dryRun );
249+ return SendResponse .fromMessageId (messageId );
250+ } catch (FirebaseMessagingException e ) {
251+ return SendResponse .fromException (e );
252+ }
253+ }
254+ };
255+ }
256+
257+ /**
258+ * Sends the given multicast message to all the FCM registration tokens specified in it.
259+ *
260+ * <p>This method uses the {@link #sendEach(List)} API under the hood to send the given
261+ * message to all the target recipients. The responses list obtained by calling
262+ * {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens
263+ * in the {@link MulticastMessage}.
264+ *
265+ * @param message A non-null {@link MulticastMessage}
266+ * @return A {@link BatchResponse} indicating the result of the operation.
267+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
268+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
269+ * failure -- i.e. none of the messages in the list could be sent. Partial failures or no
270+ * failures are only indicated by a {@link BatchResponse}.
271+ */
272+ public BatchResponse sendEachForMulticast (
273+ @ NonNull MulticastMessage message ) throws FirebaseMessagingException {
274+ return sendEachForMulticast (message , false );
275+ }
276+
277+ /**
278+ * Sends the given multicast message to all the FCM registration tokens specified in it.
279+ *
280+ * <p>If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
281+ * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
282+ * option is useful for determining whether an FCM registration has been deleted. But it cannot be
283+ * used to validate APNs tokens.
284+ *
285+ * <p>This method uses the {@link #sendEach(List)} API under the hood to send the given
286+ * message to all the target recipients. The responses list obtained by calling
287+ * {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens
288+ * in the {@link MulticastMessage}.
289+ *
290+ * @param message A non-null {@link MulticastMessage}.
291+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
292+ * @return A {@link BatchResponse} indicating the result of the operation.
293+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
294+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
295+ * failure -- i.e. none of the messages in the list could be sent. Partial failures or no
296+ * failures are only indicated by a {@link BatchResponse}.
297+ */
298+ public BatchResponse sendEachForMulticast (@ NonNull MulticastMessage message , boolean dryRun )
299+ throws FirebaseMessagingException {
300+ checkNotNull (message , "multicast message must not be null" );
301+ return sendEach (message .getMessageList (), dryRun );
302+ }
303+
304+ /**
305+ * Similar to {@link #sendEachForMulticast(MulticastMessage)} but performs the operation
306+ * asynchronously.
307+ *
308+ * @param message A non-null {@link MulticastMessage}.
309+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
310+ * the messages have been sent.
311+ */
312+ public ApiFuture <BatchResponse > sendEachForMulticastAsync (@ NonNull MulticastMessage message ) {
313+ return sendEachForMulticastAsync (message , false );
314+ }
315+
316+ /**
317+ * Similar to {@link #sendEachForMulticast(MulticastMessage, boolean)} but performs the operation
318+ * asynchronously.
319+ *
320+ * @param message A non-null {@link MulticastMessage}.
321+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
322+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
323+ * the messages have been sent.
324+ */
325+ public ApiFuture <BatchResponse > sendEachForMulticastAsync (
326+ @ NonNull MulticastMessage message , boolean dryRun ) {
327+ checkNotNull (message , "multicast message must not be null" );
328+ return sendEachAsync (message .getMessageList (), dryRun );
329+ }
330+
142331 /**
143332 * Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to
144333 * send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this
@@ -152,6 +341,7 @@ protected String execute() throws FirebaseMessagingException {
152341 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
153342 * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
154343 * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
344+ * @deprecated Use {@link #sendEach(List)} instead.
155345 */
156346 public BatchResponse sendAll (
157347 @ NonNull List <Message > messages ) throws FirebaseMessagingException {
@@ -177,6 +367,7 @@ public BatchResponse sendAll(
177367 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
178368 * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
179369 * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
370+ * @deprecated Use {@link #sendEach(List, boolean)} instead.
180371 */
181372 public BatchResponse sendAll (
182373 @ NonNull List <Message > messages , boolean dryRun ) throws FirebaseMessagingException {
@@ -187,8 +378,9 @@ public BatchResponse sendAll(
187378 * Similar to {@link #sendAll(List)} but performs the operation asynchronously.
188379 *
189380 * @param messages A non-null, non-empty list containing up to 500 messages.
190- * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
381+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
191382 * the messages have been sent.
383+ * @deprecated Use {@link #sendEachAsync(List)} instead.
192384 */
193385 public ApiFuture <BatchResponse > sendAllAsync (@ NonNull List <Message > messages ) {
194386 return sendAllAsync (messages , false );
@@ -199,8 +391,9 @@ public ApiFuture<BatchResponse> sendAllAsync(@NonNull List<Message> messages) {
199391 *
200392 * @param messages A non-null, non-empty list containing up to 500 messages.
201393 * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
202- * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
394+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
203395 * the messages have been sent, or when the emulation has finished.
396+ * @deprecated Use {@link #sendEachAsync(List, boolean)} instead.
204397 */
205398 public ApiFuture <BatchResponse > sendAllAsync (
206399 @ NonNull List <Message > messages , boolean dryRun ) {
@@ -221,6 +414,7 @@ public ApiFuture<BatchResponse> sendAllAsync(
221414 * delivery. An exception here indicates a total failure -- i.e. the messages could not be
222415 * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
223416 * return value.
417+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage)} instead.
224418 */
225419 public BatchResponse sendMulticast (
226420 @ NonNull MulticastMessage message ) throws FirebaseMessagingException {
@@ -247,6 +441,7 @@ public BatchResponse sendMulticast(
247441 * delivery. An exception here indicates a total failure -- i.e. the messages could not be
248442 * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
249443 * return value.
444+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage, boolean)} instead.
250445 */
251446 public BatchResponse sendMulticast (
252447 @ NonNull MulticastMessage message , boolean dryRun ) throws FirebaseMessagingException {
@@ -261,6 +456,7 @@ public BatchResponse sendMulticast(
261456 * @param message A non-null {@link MulticastMessage}.
262457 * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
263458 * the messages have been sent.
459+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage)} instead.
264460 */
265461 public ApiFuture <BatchResponse > sendMulticastAsync (@ NonNull MulticastMessage message ) {
266462 return sendMulticastAsync (message , false );
@@ -274,6 +470,7 @@ public ApiFuture<BatchResponse> sendMulticastAsync(@NonNull MulticastMessage mes
274470 * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
275471 * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
276472 * the messages have been sent.
473+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage, boolean)} instead.
277474 */
278475 public ApiFuture <BatchResponse > sendMulticastAsync (
279476 @ NonNull MulticastMessage message , boolean dryRun ) {
0 commit comments