since today I encountered the following issue with GCM "subscribe to topics".
Nexus 6, Android 6.0.1, Google Play Services 9.0.83
Using google-play-services:8.3.0 in app.
Step 1
I follow the documentation from Google for getting the token through the instance id.
After getting the token I successfully subscribe to the "topics/global" topic and store the token in the shared preferences.
protected void register() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
// [START register_for_gcm]
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
// R.string.gcm_defaultSenderId (the Sender ID) is typically derived from google-services.json.
// See https://developers.google.com/cloud-messaging/android/start for details on this file.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
sharedPreferences.edit().putString("token", token).apply();
// You should store a boolean that indicates whether the generated token has been
// sent to your server. If the boolean is false, send the token to your server,
// otherwise your server should have already received the token.
sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, true).apply();
// [END register_for_gcm]
} catch (Exception e) {
Log.d(TAG, "Failed to complete token refresh", e);
// If an exception happens while fetching the new token or updating our registration data
// on a third-party server, this ensures that we'll attempt the update at a later time.
sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(QuickstartPreferences.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
Step 2
After some time / on user interaction I want to subscribe to additional topics.
I fetch the token from the shared preferences and try to subscribe like before, but this time it fails with the "java.io.IOException: InternalServerError".
The exception is catched of course, but I do not know how to proceed now.
private void subscribeTopics() throws IOException {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String token = sharedPreferences.getString("token", null);
if(token == null) {
Log.e(TAG, "No token");
return;
}
GcmPubSub pubSub = GcmPubSub.getInstance(this);
for (String topic : TOPICS) {
pubSub.subscribe(token, "/topics/" + topic, null); // <--- FAILS HERE
}
Log.d(TAG, "Subscribed to topics.");
}
This process worked for the last 5 months without issues. Suddenly, since this morning, the subscription to additional topics (step 2) fails.
Any idea if the switch to Firebase Cloud Messaging (FCM) brought breaking changes?
Currently all my client apps are not usable.
Fast help is really appreciated.
I am part of the Google Cloud Messaging team.
We identified an issue in our backed that affected a small percentage of the topic subscriptions during the last 24 hours.
The issue has already been fixed, and the subscriptions should work correctly on all devices.
Please let us know if you are still experiencing this error.
Thanks Steffen for reporting the issue.
Related
I'm working on an authentication plugin that uses JWT parsing to get details and update the user in Mesh.
I'd like to also create a new node and attach it to the User in Mesh, using the user.setNodeReference() // Is this how I associate a User to a node?
The problem is when I return the mapping result, if I create the user profile node, I see the mapToken() method invoked again with the same token as before, like it's looping. I've found this is due to the 'retry' capabilities in the router
If I dont attach a node to the user.nodeReference() then it proceeds as expected.
Thoughts?
#Override
public MappingResult mapToken(HttpServerRequest req, String uuid, JsonObject token) {
MappingResult result = new MappingResult();
if (uuid == null) {
log.info("First time login of the user");
} else {
log.info("Already synced user is logging in.");
}
log.info("Mapping user in plugin");
printToken(token);
String username = extractUsername(token).get();
UserUpdateRequest user = new UserUpdateRequest();
user.setUsername(username);
user.setEmailAddress(username);
user.setFirstname(token.getString("firstname", "firstname"));
user.setLastname(token.getString("lastname", "lastname"));
// TODO: Stop the infinite loop
if (uuid == null) {
log.info("Creating profile node");
user.setNodeReference(createProfileNode(username, token));
} else {
log.info("Updating profile node");
//updateProfileNode(uuid, token);
}
result.setUser(user);
...
}
private ExpandableNode createProfileNode(String username, JsonObject token) {
NodeCreateRequest nodeCreateRequest = new NodeCreateRequest()
.setLanguage("en")
.setSchemaName(getConfig().getProfileSchema())
.setParentNodeUuid(getConfig().getProfileParentUuid());
FieldMap fields = nodeCreateRequest.getFields();
fields.putString("name", username);
fillProfileFieldMappedValues(fields, token);
nodeCreateRequest.setFields(fields);
return this.adminClient.createNode(getConfig().getProjectName(), nodeCreateRequest).blockingGet();
}
Update
I checked the jti & iat - the token contains both.
I thought maybe if I subscribe to the USER_CREATED event, I could add a profile node after the user is created.
But I don't see this ever executed. I may be incorrectly subscribing to the local event bus.
getRxVertx().eventBus().localConsumer(MeshEvent.USER_CREATED.getAddress()).handler((message) -> {
try {
String uuid = JsonUtil.getMapper().readTree(message.body().toString()).get("uuid").asText();
adminClient().findUserByUuid(uuid).toSingle().doAfterSuccess(u -> {
u.setNodeReference(createProfileNode(u.getUuid()).getBody());
}).doOnError(e -> {
log.error("Failed to create user profile node: {}", e);
});
} catch (IOException e) {
log.error("Failed to deserialize user: {}", e);
}
});
Also, I don't need to set the user.setNodeReference() to reproduce the error, I only need to try creating a new node in the mapToken method. It will retry creating the user 10x then error out with an http 500.
I'll turn up logging to see if I can get more details.
Update
I've found that if I create the user first in the mapToken function, then create a node for the profile, I can add it to the user.setNodeReference() but I never see the node in the content browser [I create it at `{project}/profiles/{userProfileNode}], and I'm not able to see the node reference when I retrieve the user.
But the logs show the node was created successfully.
Does your token contain a token Id? (jti or iat). Mesh will use one of these values to determine whether the key mapping needs to be re-run for the token. The idea behind this is to avoid bogus mapping calls for tokens that have not changed. I suspect your token does not pass this check and will be passed always to the mapper plugin.
I might be able to give you more hints if I could see some logs.
I am trying to get the device token once the person has logged into the app and send back the device token to our server. The login process is done in async.
According to this post , you can get the token in activities but is it possible to get in async?
I have tried to put the code in async but does not work. (I changed the MainActivity.this to (Activity)mycontext, where I have defined mycontext as a Context)
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( (Activitiy) mycontext, new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String newToken = instanceIdResult.getToken();
Log.e("newToken",newToken);
}
});
Thank you very much
thanks to Shashanth , the solution is just to get the token in one activity and pass that token value to async
I want to generate and store token everytime users login. I tried for my code below, but it always generate same token when I login with another account.
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
AlertDialog.dismiss();
if (!task.isSuccessful()) {
if (password.length() < 6) {
new SweetAlertDialog(LoginActivity.this, SweetAlertDialog.ERROR_TYPE)
.setTitleText("Oops...")
.setContentText("Enter minimum 6 charachters !! ")
.show();
} else {
passwordInput.setText("");
new SweetAlertDialog(LoginActivity.this, SweetAlertDialog.ERROR_TYPE)
.setTitleText("Oops...")
.setContentText("Authentication failed !!")
.show();
}
} else {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
FirebaseUser users = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("users/"+ users.getUid());
String token = FirebaseInstanceId.getInstance().getToken();
Log.e("tokenid",""+token);
mDatabase.child("token_id").setValue(token);
finish();
}
}
});
}
});
please help, thanks..
When using the following line of code:
String token = FirebaseInstanceId.getInstance().getToken();
You are getting the Firebase Instance ID token, which is also known as FCM token, on the client side. You need to know that in Firebase there two different tokens:
A FCM Token/Instance ID token that identifies an installation of a specific application on a specific user device. It doesn't identify in any way a particular user.
An Auth ID token identifies a specific user of a specific application. It doesn't identify in any way a particular device.
The two tokens are quite different and serve different purposes. Please see the official documentation for Android regarding on how to retrieve ID tokens on clients.
The identity of the user is remembered indefinitely (or until you sign out). But their credentials (or rather, whether their token is still valid) are re-checked every hour.
FCM generates a registration token for the client app instance, hence It may happen that you'll get the same token for different users in your app. You can use forceRefresh to generate a new token every time. Register new token everytime user logins to any device and save it in DB and update with a new token on new login this way you will have a new token for each user on every login (If this fits your requirement)
Here is a good answer to understand how it works Firebase FCM force onTokenRefresh() to be called
Use UUID.randomUUID().toString()
You can read more here.
java docs - and here =)
I'm having a hard time getting responses to my questions lately and I'm not why. If I am not asking right, can someone please let me know so I can adjust?
I am building an app with Ionic but having issues sending badge numbers for Android via FCM. I can't find anything in the Firebase Docs. How do I set the badge count when a message is received?
Here is my thought process but I don't know how to get it to work:
when the message is received, use the ionic native badge plugin to set it
send the badge count using FCM payload
NOTE: I am using NodeJS implementation for Firebase
What I've tried
I am using the cordova-plugin-fcm plugin and the Android ShortcutBadger to set the badge, but I am unable to set the badge when the message is received. I was only able to set it when the app is already open by calling the shortcutbadger function in the MyFirebaseMessagingService java class (of the cordova-plugin-fcm). Below is the code I am using in my FCMPluginActivity java class:
// more imports here
import me.leolin.shortcutbadger.ShortcutBadger;
public class FCMPluginActivity extends Activity {
private static String TAG = "FCMPlugin";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "==> FCMPluginActivity onCreate");
Map<String, Object> data = new HashMap<String, Object>();
if (getIntent().getExtras() != null) {
Log.d(TAG, "==> USER TAPPED NOTFICATION");
data.put("wasTapped", true);
for (String key : getIntent().getExtras().keySet()) {
String value = getIntent().getExtras().getString(key);
Log.d(TAG, "\tKey: " + key + " Value: " + value);
data.put(key, value);
}
}
FCMPlugin.sendPushPayload(data);
ShortcutBadger.applyCount(FCMPluginActivity.this, 8);
finish();
forceMainActivityReload();
}
I am importing ShortcutBadger and using it in the onCreate method. The idea is to set the badger when the notification is received; however, this is not working.
Your thoughts and comments are very much appreciated
Set the badge field when you send the notification. The documentation specifies this for ios and based on my test it works, but with some delay. I would also try it for Android, but it may not be possible:
Search for 'badge' here:
https://firebase.google.com/docs/cloud-messaging/http-server-ref
I have an Android and iOS application that uses Firebase cloud functions with Stripe to process payments.
On the client side I handle the token operation and then write to the realtime database. When done writing the addPaymentSource cloud function triggers which stores that payment source for future transactions.
Interesting enough the process of creating a token on iOS and then saving that output to my server works as expected. My problem comes along when trying to duplicate my iOS implementation into my Android application. The Firebase cloud function triggers as expected but it is outputting an error to my server.
Error found in server:
"The source hash must include an 'object' key indicating what type of source to create."
Client code:
public void tokenizePaymentFields(){
Stripe stripe = new Stripe(getApplicationContext(), stripePublishableKey);
final Card stripeCard = new Card(validCard.getCardNumber()
,Integer.valueOf(validCard.getExpiredDate().substring(0,2)),Integer.valueOf(validCard.getExpiredDate().substring(3,5)),validCard.getCvvCode());
if (!stripeCard.validateCard()) {
Toast.makeText(getApplicationContext(),
"There was an error validating your card.",
Toast.LENGTH_LONG
).show();
return;
}
stripe.createToken(
stripeCard,
new TokenCallback() {
public void onSuccess(Token token) {
// Send token to your server
pushToServer(token);
}
public void onError(Exception error) {
// Show localized error message
activitySubmitCreditCardBinding.progressCircle.setVisibility(View.INVISIBLE);
Toast.makeText(getApplicationContext(),
error.getLocalizedMessage(),
Toast.LENGTH_LONG
).show();
}
}
);
}
Stripe(Firebase Cloud Functions):
https://github.com/firebase/functions-samples/tree/master/stripe
Rather than sending the entire token object to your server, you should simply send the token's id, like this:
public void onSuccess(Token token) {
// Send token to your server
pushToServer(token.getId());
}
In your server-side (Firebase) code, the charge creation request expects only a token ID in the source parameter, not a full token object.