com.google.firebase.messaging.FirebaseMessaging Maven / Gradle / Ivy
Show all versions of firebase-admin Show documentation
/*
* Copyright 2018 Google Inc.
*
* 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.
*/
package com.google.firebase.messaging;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.internal.CallableOperation;
import com.google.firebase.internal.FirebaseService;
import com.google.firebase.internal.NonNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* This class is the entry point for all server-side Firebase Cloud Messaging actions.
*
* You can get an instance of FirebaseMessaging via {@link #getInstance(FirebaseApp)}, and
* then use it to send messages or manage FCM topic subscriptions.
*/
public class FirebaseMessaging {
private final FirebaseApp app;
private final Supplier extends FirebaseMessagingClient> messagingClient;
private final Supplier extends InstanceIdClient> instanceIdClient;
private FirebaseMessaging(Builder builder) {
this.app = checkNotNull(builder.firebaseApp);
this.messagingClient = Suppliers.memoize(builder.messagingClient);
this.instanceIdClient = Suppliers.memoize(builder.instanceIdClient);
}
/**
* Gets the {@link FirebaseMessaging} instance for the default {@link FirebaseApp}.
*
* @return The {@link FirebaseMessaging} instance for the default {@link FirebaseApp}.
*/
public static FirebaseMessaging getInstance() {
return getInstance(FirebaseApp.getInstance());
}
/**
* Gets the {@link FirebaseMessaging} instance for the specified {@link FirebaseApp}.
*
* @return The {@link FirebaseMessaging} instance for the specified {@link FirebaseApp}.
*/
public static synchronized FirebaseMessaging getInstance(FirebaseApp app) {
FirebaseMessagingService service = ImplFirebaseTrampolines.getService(app, SERVICE_ID,
FirebaseMessagingService.class);
if (service == null) {
service = ImplFirebaseTrampolines.addService(app, new FirebaseMessagingService(app));
}
return service.getInstance();
}
/**
* Sends the given {@link Message} via Firebase Cloud Messaging.
*
* @param message A non-null {@link Message} to be sent.
* @return A message ID string.
* @throws FirebaseMessagingException If an error occurs while handing the message off to FCM for
* delivery.
*/
public String send(@NonNull Message message) throws FirebaseMessagingException {
return send(message, false);
}
/**
* Sends the given {@link Message} via Firebase Cloud Messaging.
*
*
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. However, it
* cannot be used to validate APNs tokens.
*
* @param message A non-null {@link Message} to be sent.
* @param dryRun a boolean indicating whether to perform a dry run (validation only) of the send.
* @return A message ID string.
* @throws FirebaseMessagingException If an error occurs while handing the message off to FCM for
* delivery.
*/
public String send(@NonNull Message message, boolean dryRun) throws FirebaseMessagingException {
return sendOp(message, dryRun).call();
}
/**
* Similar to {@link #send(Message)} but performs the operation asynchronously.
*
* @param message A non-null {@link Message} to be sent.
* @return An {@code ApiFuture} that will complete with a message ID string when the message
* has been sent.
*/
public ApiFuture sendAsync(@NonNull Message message) {
return sendAsync(message, false);
}
/**
* Similar to {@link #send(Message, boolean)} but performs the operation asynchronously.
*
* @param message A non-null {@link Message} to be sent.
* @param dryRun a boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a message ID string when the message
* has been sent, or when the emulation has finished.
*/
public ApiFuture sendAsync(@NonNull Message message, boolean dryRun) {
return sendOp(message, dryRun).callAsync(app);
}
private CallableOperation sendOp(
final Message message, final boolean dryRun) {
checkNotNull(message, "message must not be null");
final FirebaseMessagingClient messagingClient = getMessagingClient();
return new CallableOperation() {
@Override
protected String execute() throws FirebaseMessagingException {
return messagingClient.send(message, dryRun);
}
};
}
/**
* Sends each message in the given list via Firebase Cloud Messaging.
* Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the
* given array.
*
* The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
* value is in the same order as the input list.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
* failure, meaning that none of the messages in the list could be sent. Partial failures or
* no failures are only indicated by a {@link BatchResponse}.
*/
public BatchResponse sendEach(@NonNull List messages) throws FirebaseMessagingException {
return sendEach(messages, false);
}
/**
* Sends each message in the given list via Firebase Cloud Messaging.
* Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the
* given array.
*
* 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.
*
*
The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
* value is in the same order as the input list.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
* failure, meaning that none of the messages in the list could be sent. Partial failures or
* no failures are only indicated by a {@link BatchResponse}.
*/
public BatchResponse sendEach(
@NonNull List messages, boolean dryRun) throws FirebaseMessagingException {
try {
return sendEachOpAsync(messages, dryRun).get();
} catch (InterruptedException | ExecutionException e) {
throw new FirebaseMessagingException(ErrorCode.CANCELLED, SERVICE_ID);
}
}
/**
* Similar to {@link #sendEach(List)} but performs the operation asynchronously.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
public ApiFuture sendEachAsync(@NonNull List messages) {
return sendEachOpAsync(messages, false);
}
/**
* Similar to {@link #sendEach(List, boolean)} but performs the operation asynchronously.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
public ApiFuture sendEachAsync(@NonNull List messages, boolean dryRun) {
return sendEachOpAsync(messages, dryRun);
}
// Returns an ApiFuture directly since this function is non-blocking. Individual child send
// requests are still called async and run in background threads.
private ApiFuture sendEachOpAsync(
final List messages, final boolean dryRun) {
final List immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
checkArgument(immutableMessages.size() <= 500,
"messages list must not contain more than 500 elements");
List> list = new ArrayList<>();
for (Message message : immutableMessages) {
// Make async send calls per message
ApiFuture messageId = sendOpForSendResponse(message, dryRun).callAsync(app);
list.add(messageId);
}
// Gather all futures and combine into a list
ApiFuture> responsesFuture = ApiFutures.allAsList(list);
// Chain this future to wrap the eventual responses in a BatchResponse without blocking
// the main thread. This uses the current thread to execute, but since the transformation
// function is non-blocking the transformation itself is also non-blocking.
return ApiFutures.transform(
responsesFuture,
(responses) -> {
return new BatchResponseImpl(responses);
},
MoreExecutors.directExecutor());
}
private CallableOperation sendOpForSendResponse(
final Message message, final boolean dryRun) {
checkNotNull(message, "message must not be null");
final FirebaseMessagingClient messagingClient = getMessagingClient();
return new CallableOperation() {
@Override
protected SendResponse execute() {
try {
String messageId = messagingClient.send(message, dryRun);
return SendResponse.fromMessageId(messageId);
} catch (FirebaseMessagingException e) {
return SendResponse.fromException(e);
}
}
};
}
/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
*
* 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}.
*
* @param message A non-null {@link MulticastMessage}
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
* failure, meaning that none of the messages in the list could be sent. Partial failures or
* no failures are only indicated by a {@link BatchResponse}.
*/
public BatchResponse sendEachForMulticast(
@NonNull MulticastMessage message) throws FirebaseMessagingException {
return sendEachForMulticast(message, false);
}
/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
*
*
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.
*
*
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}.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
* failure, meaning that none of the messages in the list could be sent. Partial failures or
* no failures are only indicated by a {@link BatchResponse}.
*/
public BatchResponse sendEachForMulticast(@NonNull MulticastMessage message, boolean dryRun)
throws FirebaseMessagingException {
checkNotNull(message, "multicast message must not be null");
return sendEach(message.getMessageList(), dryRun);
}
/**
* Similar to {@link #sendEachForMulticast(MulticastMessage)} but performs the operation
* asynchronously.
*
* @param message A non-null {@link MulticastMessage}.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
public ApiFuture sendEachForMulticastAsync(@NonNull MulticastMessage message) {
return sendEachForMulticastAsync(message, false);
}
/**
* Similar to {@link #sendEachForMulticast(MulticastMessage, boolean)} but performs the operation
* asynchronously.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
*/
public ApiFuture sendEachForMulticastAsync(
@NonNull MulticastMessage message, boolean dryRun) {
checkNotNull(message, "multicast message must not be null");
return sendEachAsync(message.getMessageList(), dryRun);
}
/**
* Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to
* send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this
* is a significantly more efficient way to send multiple messages.
*
* The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
* value corresponds to the order of input messages.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here indicates a total failure, meaning that none of the messages in
* the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
* value.
* @deprecated Use {@link #sendEach(List)} instead.
*/
@Deprecated
public BatchResponse sendAll(
@NonNull List messages) throws FirebaseMessagingException {
return sendAll(messages, false);
}
/**
* Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to
* send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this
* is a significantly more efficient way to send multiple messages.
*
* 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.
*
*
The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
* value corresponds to the order of input messages.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here indicates a total failure, meaning that none of the messages in
* the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
* value.
* @deprecated Use {@link #sendEach(List, boolean)} instead.
*/
@Deprecated
public BatchResponse sendAll(
@NonNull List messages, boolean dryRun) throws FirebaseMessagingException {
return sendAllOp(messages, dryRun).call();
}
/**
* Similar to {@link #sendAll(List)} but performs the operation asynchronously.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
* @deprecated Use {@link #sendEachAsync(List)} instead.
*/
@Deprecated
public ApiFuture sendAllAsync(@NonNull List messages) {
return sendAllAsync(messages, false);
}
/**
* Similar to {@link #sendAll(List, boolean)} but performs the operation asynchronously.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent, or when the emulation has finished.
* @deprecated Use {@link #sendEachAsync(List, boolean)} instead.
*/
@Deprecated
public ApiFuture sendAllAsync(
@NonNull List messages, boolean dryRun) {
return sendAllOp(messages, dryRun).callAsync(app);
}
/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
*
* 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}.
*
* @param message A non-null {@link MulticastMessage}
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here indicates a total failure, meaning that the messages could not
* be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
* return value.
* @deprecated Use {@link #sendEachForMulticast(MulticastMessage)} instead.
*/
@Deprecated
public BatchResponse sendMulticast(
@NonNull MulticastMessage message) throws FirebaseMessagingException {
return sendMulticast(message, false);
}
/**
* Sends the given multicast message to all the FCM registration tokens specified in it.
*
*
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.
*
*
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}.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
* delivery. An exception here indicates a total failure, meaning that the messages could not
* be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
* return value.
* @deprecated Use {@link #sendEachForMulticast(MulticastMessage, boolean)} instead.
*/
@Deprecated
public BatchResponse sendMulticast(
@NonNull MulticastMessage message, boolean dryRun) throws FirebaseMessagingException {
checkNotNull(message, "multicast message must not be null");
return sendAll(message.getMessageList(), dryRun);
}
/**
* Similar to {@link #sendMulticast(MulticastMessage)} but performs the operation
* asynchronously.
*
* @param message A non-null {@link MulticastMessage}.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
* @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage)} instead.
*/
@Deprecated
public ApiFuture sendMulticastAsync(@NonNull MulticastMessage message) {
return sendMulticastAsync(message, false);
}
/**
* Similar to {@link #sendMulticast(MulticastMessage, boolean)} but performs the operation
* asynchronously.
*
* @param message A non-null {@link MulticastMessage}.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
* @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage, boolean)} instead.
*/
@Deprecated
public ApiFuture sendMulticastAsync(
@NonNull MulticastMessage message, boolean dryRun) {
checkNotNull(message, "multicast message must not be null");
return sendAllAsync(message.getMessageList(), dryRun);
}
private CallableOperation sendAllOp(
final List messages, final boolean dryRun) {
final List immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
checkArgument(immutableMessages.size() <= 500,
"messages list must not contain more than 500 elements");
final FirebaseMessagingClient messagingClient = getMessagingClient();
return new CallableOperation() {
@Override
protected BatchResponse execute() throws FirebaseMessagingException {
return messagingClient.sendAll(messages, dryRun);
}
};
}
@VisibleForTesting
FirebaseMessagingClient getMessagingClient() {
return messagingClient.get();
}
/**
* Subscribes a list of registration tokens to a topic.
*
* @param registrationTokens A non-null, non-empty list of device registration tokens, with at
* most 1000 entries.
* @param topic Name of the topic to subscribe to. May contain the {@code /topics/} prefix.
* @return A {@link TopicManagementResponse}.
*/
public TopicManagementResponse subscribeToTopic(@NonNull List registrationTokens,
@NonNull String topic) throws FirebaseMessagingException {
return subscribeOp(registrationTokens, topic).call();
}
/**
* Similar to {@link #subscribeToTopic(List, String)} but performs the operation asynchronously.
*
* @param registrationTokens A non-null, non-empty list of device registration tokens, with at
* most 1000 entries.
* @param topic Name of the topic to subscribe to. May contain the {@code /topics/} prefix.
* @return An {@code ApiFuture} that will complete with a {@link TopicManagementResponse}.
*/
public ApiFuture subscribeToTopicAsync(
@NonNull List registrationTokens, @NonNull String topic) {
return subscribeOp(registrationTokens, topic).callAsync(app);
}
private CallableOperation subscribeOp(
final List registrationTokens, final String topic) {
checkRegistrationTokens(registrationTokens);
checkTopic(topic);
final InstanceIdClient instanceIdClient = getInstanceIdClient();
return new CallableOperation() {
@Override
protected TopicManagementResponse execute() throws FirebaseMessagingException {
return instanceIdClient.subscribeToTopic(topic, registrationTokens);
}
};
}
/**
* Unsubscribes a list of registration tokens from a topic.
*
* @param registrationTokens A non-null, non-empty list of device registration tokens, with at
* most 1000 entries.
* @param topic Name of the topic to unsubscribe from. May contain the {@code /topics/} prefix.
* @return A {@link TopicManagementResponse}.
*/
public TopicManagementResponse unsubscribeFromTopic(@NonNull List registrationTokens,
@NonNull String topic) throws FirebaseMessagingException {
return unsubscribeOp(registrationTokens, topic).call();
}
/**
* Similar to {@link #unsubscribeFromTopic(List, String)} but performs the operation
* asynchronously.
*
* @param registrationTokens A non-null, non-empty list of device registration tokens, with at
* most 1000 entries.
* @param topic Name of the topic to unsubscribe from. May contain the {@code /topics/} prefix.
* @return An {@code ApiFuture} that will complete with a {@link TopicManagementResponse}.
*/
public ApiFuture unsubscribeFromTopicAsync(
@NonNull List registrationTokens, @NonNull String topic) {
return unsubscribeOp(registrationTokens, topic).callAsync(app);
}
private CallableOperation unsubscribeOp(
final List registrationTokens, final String topic) {
checkRegistrationTokens(registrationTokens);
checkTopic(topic);
final InstanceIdClient instanceIdClient = getInstanceIdClient();
return new CallableOperation() {
@Override
protected TopicManagementResponse execute() throws FirebaseMessagingException {
return instanceIdClient.unsubscribeFromTopic(topic, registrationTokens);
}
};
}
@VisibleForTesting
InstanceIdClient getInstanceIdClient() {
return this.instanceIdClient.get();
}
private void checkRegistrationTokens(List registrationTokens) {
checkArgument(registrationTokens != null && !registrationTokens.isEmpty(),
"registrationTokens list must not be null or empty");
checkArgument(registrationTokens.size() <= 1000,
"registrationTokens list must not contain more than 1000 elements");
for (String token : registrationTokens) {
checkArgument(!Strings.isNullOrEmpty(token),
"registration tokens list must not contain null or empty strings");
}
}
private void checkTopic(String topic) {
checkArgument(!Strings.isNullOrEmpty(topic), "topic must not be null or empty");
checkArgument(topic.matches("^(/topics/)?(private/)?[a-zA-Z0-9-_.~%]+$"), "invalid topic name");
}
private static final String SERVICE_ID = FirebaseMessaging.class.getName();
private static class FirebaseMessagingService extends FirebaseService {
FirebaseMessagingService(FirebaseApp app) {
super(SERVICE_ID, FirebaseMessaging.fromApp(app));
}
}
private static FirebaseMessaging fromApp(final FirebaseApp app) {
return FirebaseMessaging.builder()
.setFirebaseApp(app)
.setMessagingClient(new Supplier() {
@Override
public FirebaseMessagingClient get() {
return FirebaseMessagingClientImpl.fromApp(app);
}
})
.setInstanceIdClient(new Supplier() {
@Override
public InstanceIdClientImpl get() {
return InstanceIdClientImpl.fromApp(app);
}
})
.build();
}
static Builder builder() {
return new Builder();
}
static class Builder {
private FirebaseApp firebaseApp;
private Supplier extends FirebaseMessagingClient> messagingClient;
private Supplier extends InstanceIdClient> instanceIdClient;
private Builder() { }
Builder setFirebaseApp(FirebaseApp firebaseApp) {
this.firebaseApp = firebaseApp;
return this;
}
Builder setMessagingClient(Supplier extends FirebaseMessagingClient> messagingClient) {
this.messagingClient = messagingClient;
return this;
}
Builder setInstanceIdClient(Supplier extends InstanceIdClient> instanceIdClient) {
this.instanceIdClient = instanceIdClient;
return this;
}
FirebaseMessaging build() {
return new FirebaseMessaging(this);
}
}
}