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 an HTTP call for each message in the
149+ * given array.
150+ *
151+ * <p>The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
152+ * value is in the same order as the input list.
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, meaning that none of the messages in the list could be sent. Partial failures or
159+ * no 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 an 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 list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
177+ * value is in the same order as the input list.
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, meaning that none of the messages in the list could be sent. Partial failures or
185+ * no 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 list of responses obtained by calling
262+ * {@link BatchResponse#getResponses()} on the return value is in the same order as the
263+ * tokens 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, meaning that none of the messages in the list could be sent. Partial failures or
270+ * no 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 list of responses obtained by calling
287+ * {@link BatchResponse#getResponses()} on the return value is in the same order as the
288+ * tokens 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, meaning that none of the messages in the list could be sent. Partial failures or
296+ * no 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
@@ -150,8 +339,10 @@ protected String execute() throws FirebaseMessagingException {
150339 * @param messages A non-null, non-empty list containing up to 500 messages.
151340 * @return A {@link BatchResponse} indicating the result of the operation.
152341 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
153- * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
154- * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
342+ * delivery. An exception here indicates a total failure, meaning that none of the messages in
343+ * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
344+ * value.
345+ * @deprecated Use {@link #sendEach(List)} instead.
155346 */
156347 public BatchResponse sendAll (
157348 @ NonNull List <Message > messages ) throws FirebaseMessagingException {
@@ -175,8 +366,10 @@ public BatchResponse sendAll(
175366 * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
176367 * @return A {@link BatchResponse} indicating the result of the operation.
177368 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
178- * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
179- * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
369+ * delivery. An exception here indicates a total failure, meaning that none of the messages in
370+ * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
371+ * value.
372+ * @deprecated Use {@link #sendEach(List, boolean)} instead.
180373 */
181374 public BatchResponse sendAll (
182375 @ NonNull List <Message > messages , boolean dryRun ) throws FirebaseMessagingException {
@@ -187,8 +380,9 @@ public BatchResponse sendAll(
187380 * Similar to {@link #sendAll(List)} but performs the operation asynchronously.
188381 *
189382 * @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
383+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
191384 * the messages have been sent.
385+ * @deprecated Use {@link #sendEachAsync(List)} instead.
192386 */
193387 public ApiFuture <BatchResponse > sendAllAsync (@ NonNull List <Message > messages ) {
194388 return sendAllAsync (messages , false );
@@ -199,8 +393,9 @@ public ApiFuture<BatchResponse> sendAllAsync(@NonNull List<Message> messages) {
199393 *
200394 * @param messages A non-null, non-empty list containing up to 500 messages.
201395 * @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
396+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
203397 * the messages have been sent, or when the emulation has finished.
398+ * @deprecated Use {@link #sendEachAsync(List, boolean)} instead.
204399 */
205400 public ApiFuture <BatchResponse > sendAllAsync (
206401 @ NonNull List <Message > messages , boolean dryRun ) {
@@ -218,9 +413,10 @@ public ApiFuture<BatchResponse> sendAllAsync(
218413 * @param message A non-null {@link MulticastMessage}
219414 * @return A {@link BatchResponse} indicating the result of the operation.
220415 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
221- * delivery. An exception here indicates a total failure -- i.e. the messages could not be
222- * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
416+ * delivery. An exception here indicates a total failure, meaning that the messages could not
417+ * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
223418 * return value.
419+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage)} instead.
224420 */
225421 public BatchResponse sendMulticast (
226422 @ NonNull MulticastMessage message ) throws FirebaseMessagingException {
@@ -244,9 +440,10 @@ public BatchResponse sendMulticast(
244440 * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
245441 * @return A {@link BatchResponse} indicating the result of the operation.
246442 * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
247- * delivery. An exception here indicates a total failure -- i.e. the messages could not be
248- * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
443+ * delivery. An exception here indicates a total failure, meaning that the messages could not
444+ * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
249445 * return value.
446+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage, boolean)} instead.
250447 */
251448 public BatchResponse sendMulticast (
252449 @ NonNull MulticastMessage message , boolean dryRun ) throws FirebaseMessagingException {
@@ -261,6 +458,7 @@ public BatchResponse sendMulticast(
261458 * @param message A non-null {@link MulticastMessage}.
262459 * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
263460 * the messages have been sent.
461+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage)} instead.
264462 */
265463 public ApiFuture <BatchResponse > sendMulticastAsync (@ NonNull MulticastMessage message ) {
266464 return sendMulticastAsync (message , false );
@@ -274,6 +472,7 @@ public ApiFuture<BatchResponse> sendMulticastAsync(@NonNull MulticastMessage mes
274472 * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
275473 * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
276474 * the messages have been sent.
475+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage, boolean)} instead.
277476 */
278477 public ApiFuture <BatchResponse > sendMulticastAsync (
279478 @ NonNull MulticastMessage message , boolean dryRun ) {
0 commit comments