Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,14 @@ protected SendResponse execute() {
}

/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
* Sends the given multicast message to all the FCM registration tokens and/or FIDs
* specified in it.
*
* <p>This method uses the {@link #sendEach(List)} API under the hood to send the given
* message to all the target recipients. The list of responses obtained by calling
* {@link BatchResponse#getResponses()} on the return value is in the same order as the
* tokens in the {@link MulticastMessage}.
* tokens and/or FIDs in the {@link MulticastMessage}. If both tokens and FIDs are
* provided, tokens are processed first, followed by FIDs.
*
* @param message A non-null {@link MulticastMessage}
* @return A {@link BatchResponse} indicating the result of the operation.
Expand All @@ -285,17 +287,19 @@ public BatchResponse sendEachForMulticast(
}

/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
* Sends the given multicast message to all the FCM registration tokens and/or FIDs
* specified in it.
*
* <p>If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
* FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
* option is useful for determining whether an FCM registration has been deleted. But it cannot be
* used to validate APNs tokens.
* <p>If the {@code dryRun} option is set to true, the message will not be actually sent.
* Instead FCM performs all the necessary validations, and emulates the send operation.
* The {@code dryRun} option is useful for determining whether an FCM registration has
* been deleted. But it cannot be used to validate APNs tokens.
*
* <p>This method uses the {@link #sendEach(List)} API under the hood to send the given
* message to all the target recipients. The list of responses obtained by calling
* {@link BatchResponse#getResponses()} on the return value is in the same order as the
* tokens in the {@link MulticastMessage}.
* tokens and/or FIDs in the {@link MulticastMessage}. If both tokens and FIDs are
* provided, tokens are processed first, followed by FIDs.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
Expand Down Expand Up @@ -417,12 +421,14 @@ public ApiFuture<BatchResponse> sendAllAsync(
}

/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
* Sends the given multicast message to all the FCM registration tokens and/or FIDs
* specified in it.
*
* <p>This method uses the {@link #sendAll(List)} API under the hood to send the given
* message to all the target recipients. The responses list obtained by calling
* {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens
* in the {@link MulticastMessage}.
* {@link BatchResponse#getResponses()} on the return value corresponds to the order of
* tokens and/or FIDs in the {@link MulticastMessage}. If both tokens and FIDs are
* provided, tokens are processed first, followed by FIDs.
*
* @param message A non-null {@link MulticastMessage}
* @return A {@link BatchResponse} indicating the result of the operation.
Expand All @@ -439,17 +445,19 @@ public BatchResponse sendMulticast(
}

/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
* Sends the given multicast message to all the FCM registration tokens and/or FIDs
* specified in it.
*
* <p>If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
* FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
* option is useful for determining whether an FCM registration has been deleted. But it cannot be
* used to validate APNs tokens.
* <p>If the {@code dryRun} option is set to true, the message will not be actually sent.
* Instead FCM performs all the necessary validations, and emulates the send operation.
* The {@code dryRun} option is useful for determining whether an FCM registration has
* been deleted. But it cannot be used to validate APNs tokens.
*
* <p>This method uses the {@link #sendAll(List)} API under the hood to send the given
* message to all the target recipients. The responses list obtained by calling
* {@link BatchResponse#getResponses()} on the return value corresponds to the order of tokens
* in the {@link MulticastMessage}.
* {@link BatchResponse#getResponses()} on the return value corresponds to the order of
* tokens and/or FIDs in the {@link MulticastMessage}. If both tokens and FIDs are
* provided, tokens are processed first, followed by FIDs.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
Expand Down
37 changes: 36 additions & 1 deletion src/main/java/com/google/firebase/messaging/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ public class Message {
@Key("apns")
private final ApnsConfig apnsConfig;

/**
* @deprecated Use {@link #fid} instead.
*/
@Deprecated
@Key("token")
private final String token;

@Key("fid")
private final String fid;

@Key("topic")
private final String topic;

Expand All @@ -74,11 +81,14 @@ private Message(Builder builder) {
this.apnsConfig = builder.apnsConfig;
int count = Booleans.countTrue(
!Strings.isNullOrEmpty(builder.token),
!Strings.isNullOrEmpty(builder.fid),
!Strings.isNullOrEmpty(builder.topic),
!Strings.isNullOrEmpty(builder.condition)
);
checkArgument(count == 1, "Exactly one of token, topic or condition must be specified");
checkArgument(count == 1,
"Exactly one of token, fid, topic or condition must be specified");
this.token = builder.token;
this.fid = builder.fid;
this.topic = stripPrefix(builder.topic);
this.condition = builder.condition;
this.fcmOptions = builder.fcmOptions;
Expand Down Expand Up @@ -109,11 +119,20 @@ ApnsConfig getApnsConfig() {
return apnsConfig;
}

/**
* @deprecated Use {@link #getFid()} instead.
*/
@Deprecated
@VisibleForTesting
String getToken() {
return token;
}

@VisibleForTesting
String getFid() {
return fid;
}

@VisibleForTesting
String getTopic() {
return topic;
Expand Down Expand Up @@ -166,7 +185,9 @@ public static class Builder {
private AndroidConfig androidConfig;
private WebpushConfig webpushConfig;
private ApnsConfig apnsConfig;
@Deprecated
private String token;
private String fid;
private String topic;
private String condition;
private FcmOptions fcmOptions;
Expand Down Expand Up @@ -222,12 +243,26 @@ public Builder setApnsConfig(ApnsConfig apnsConfig) {
*
* @param token A valid device registration token.
* @return This builder.
* @deprecated Use {@link #setFid(String)} instead.
*/
@Deprecated
public Builder setToken(String token) {
this.token = token;
return this;
}

/**
* Sets the Firebase Installation ID (FID) of the app instance to which the message
* should be sent.
*
* @param fid A valid Firebase Installation ID.
* @return This builder.
*/
public Builder setFid(String fid) {
this.fid = fid;
return this;
}

/**
* Sets the name of the FCM topic to which the message should be sent. Topic names may
* contain the {@code /topics/} prefix.
Expand Down
97 changes: 79 additions & 18 deletions src/main/java/com/google/firebase/messaging/MulticastMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,26 @@

/**
* Represents a message that can be sent to multiple devices via Firebase Cloud Messaging (FCM).
* Contains payload information as well as the list of device registration tokens to which the
* message should be sent. A single {@code MulticastMessage} may contain up to 500 registration
* tokens.
* Contains payload information as well as the list of device registration tokens and/or
* Firebase Installation IDs (FIDs) to which the message should be sent. A single
* {@code MulticastMessage} may contain up to 500 registration tokens and FIDs combined.
*
* <p>Instances of this class are thread-safe and immutable. Use {@link MulticastMessage.Builder}
* to create new instances. See {@link FirebaseMessaging#sendMulticast(MulticastMessage)} for
* details on how to send the message to FCM for multicast delivery.
*
* <p>This class and the associated Builder retain the order of tokens. Therefore the order of
* the responses list obtained by calling {@link BatchResponse#getResponses()} on the return value
* of {@link FirebaseMessaging#sendMulticast(MulticastMessage)} corresponds to the order in which
* tokens were added to the {@link MulticastMessage.Builder}.
* <p>This class and the associated Builder retain the order of tokens and FIDs. Therefore
* the order of the responses list obtained by calling {@link BatchResponse#getResponses()}
* on the return value of {@link FirebaseMessaging#sendMulticast(MulticastMessage)}
* corresponds to the order in which targets were added to the
* {@link MulticastMessage.Builder}. If both tokens and FIDs are provided, tokens are
* processed first, followed by FIDs.
Comment on lines +41 to +46
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Javadoc states that the response order corresponds to the order in which targets were added to the builder. However, the implementation groups tokens first and then FIDs (as noted in the subsequent sentence), which means the response order will not match the addition order if tokens and FIDs were interleaved. The documentation should be clarified to reflect that the order is preserved within each group (tokens then FIDs).

Suggested change
* <p>This class and the associated Builder retain the order of tokens and FIDs. Therefore
* the order of the responses list obtained by calling {@link BatchResponse#getResponses()}
* on the return value of {@link FirebaseMessaging#sendMulticast(MulticastMessage)}
* corresponds to the order in which targets were added to the
* {@link MulticastMessage.Builder}. If both tokens and FIDs are provided, tokens are
* processed first, followed by FIDs.
* <p>This class and the associated Builder retain the order of tokens and FIDs. Therefore
* the order of the responses list obtained by calling {@link BatchResponse#getResponses()}
* on the return value of {@link FirebaseMessaging#sendMulticast(MulticastMessage)}
* corresponds to the order of tokens followed by the order of FIDs as they were added to the
* {@link MulticastMessage.Builder}. If both tokens and FIDs are provided, tokens are
* processed first, followed by FIDs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such statement is included at the end as "If both tokens and FIDs are provided, tokens are processed first, followed by FIDs."

*/
public class MulticastMessage {

@Deprecated
private final List<String> tokens;
private final List<String> fids;
Comment thread
yvonnep165 marked this conversation as resolved.
private final Map<String, String> data;
private final Notification notification;
private final AndroidConfig androidConfig;
Expand All @@ -55,11 +59,18 @@ public class MulticastMessage {

private MulticastMessage(Builder builder) {
this.tokens = builder.tokens.build();
checkArgument(!this.tokens.isEmpty(), "at least one token must be specified");
checkArgument(this.tokens.size() <= 500, "no more than 500 tokens can be specified");
this.fids = builder.fids.build();
int tokensSize = this.tokens.size();
int fidsSize = this.fids.size();
checkArgument(tokensSize + fidsSize > 0, "at least one token or fid must be specified");
checkArgument(tokensSize + fidsSize <= 500,
"no more than 500 tokens and fids combined can be specified");
for (String token : this.tokens) {
checkArgument(!Strings.isNullOrEmpty(token), "none of the tokens can be null or empty");
}
for (String fid : this.fids) {
checkArgument(!Strings.isNullOrEmpty(fid), "none of the fids can be null or empty");
}
this.data = builder.data.isEmpty() ? null : ImmutableMap.copyOf(builder.data);
this.notification = builder.notification;
this.androidConfig = builder.androidConfig;
Expand All @@ -69,6 +80,26 @@ private MulticastMessage(Builder builder) {
}

List<Message> getMessageList() {
ImmutableList.Builder<Message> messages = ImmutableList.builder();

if (!this.tokens.isEmpty()) {
Message.Builder tokenBuilder = getMetadataBuilder();
for (String token : this.tokens) {
messages.add(tokenBuilder.setToken(token).build());
}
}

if (!this.fids.isEmpty()) {
Message.Builder fidBuilder = getMetadataBuilder();
for (String fid : this.fids) {
messages.add(fidBuilder.setFid(fid).build());
}
}

return messages.build();
}
Comment thread
yvonnep165 marked this conversation as resolved.

private Message.Builder getMetadataBuilder() {
Message.Builder builder = Message.builder()
.setNotification(this.notification)
.setAndroidConfig(this.androidConfig)
Expand All @@ -78,11 +109,7 @@ List<Message> getMessageList() {
if (this.data != null) {
builder.putAllData(this.data);
}
ImmutableList.Builder<Message> messages = ImmutableList.builder();
for (String token : this.tokens) {
messages.add(builder.setToken(token).build());
}
return messages.build();
return builder;
}

/**
Expand All @@ -96,7 +123,9 @@ public static Builder builder() {

public static class Builder {

@Deprecated
private final ImmutableList.Builder<String> tokens = ImmutableList.builder();
private final ImmutableList.Builder<String> fids = ImmutableList.builder();
private final Map<String, String> data = new HashMap<>();
private Notification notification;
private AndroidConfig androidConfig;
Expand All @@ -107,29 +136,61 @@ public static class Builder {
private Builder() {}

/**
* Adds a token to which the message should be sent. Up to 500 tokens can be specified on
* a single instance of {@link MulticastMessage}.
* Adds a token to which the message should be sent. Up to 500 tokens
* and FIDs combined can be specified on a single instance of
* {@link MulticastMessage}.
*
* @param token A non-null, non-empty Firebase device registration token.
* @return This builder.
* @deprecated Use {@link #addFid(String)} instead.
*/
@Deprecated
public Builder addToken(@NonNull String token) {
this.tokens.add(token);
return this;
}

/**
* Adds a collection of tokens to which the message should be sent. Up to 500 tokens can be
* specified on a single instance of {@link MulticastMessage}.
* Adds a Firebase Installation ID (FID) to which the message should be sent.
* Up to 500 tokens and FIDs combined can be specified on a single instance
* of {@link MulticastMessage}.
*
* @param fid A non-null, non-empty Firebase Installation ID.
* @return This builder.
*/
public Builder addFid(@NonNull String fid) {
this.fids.add(fid);
return this;
}

/**
* Adds a collection of tokens to which the message should be sent. Up to 500
* tokens and FIDs combined can be specified on a single instance of
* {@link MulticastMessage}.
*
* @param tokens Collection of Firebase device registration tokens.
* @return This builder.
* @deprecated Use {@link #addAllFids(Collection)} instead.
*/
@Deprecated
public Builder addAllTokens(@NonNull Collection<String> tokens) {
this.tokens.addAll(tokens);
return this;
}

/**
* Adds a collection of Firebase Installation IDs (FIDs) to which the message
* should be sent. Up to 500 tokens and FIDs combined can be specified on a
* single instance of {@link MulticastMessage}.
*
* @param fids Collection of Firebase Installation IDs.
* @return This builder.
*/
public Builder addAllFids(@NonNull Collection<String> fids) {
this.fids.addAll(fids);
return this;
}

/**
* Sets the notification information to be included in the message.
*
Expand Down
Loading
Loading