I am using the sameple code from developer website but get error in compiling.
http://developer.android.com/google/gcm/gs.html
copying the code below
private void registerBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
regid = gcm.register(GCM_SENDER_ID);
msg = "Device registered, registration id=" + regid;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that will echo back
// the message using the 'from' address in the message.
// Save the regid for future use - no need to register again.
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regid);
editor.commit();
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
// Once registration is done, display the registration status
// string in the Activity's UI.
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
I get the error in compiling stating "Asynctask is a raw type. Reference to generic type should be paramterised.
You have not declared generic type parameters.
change
new AsyncTask() {
to
new AsyncTask<Void,Void,String>() {
and Also,
execute(null, null, null);
can be changed to
execute();
Related
When sending upstream message most of the times the message does not get to my server, and even when the message received to the server the onMessageSent(String msgId) function isn't called (the onMessageReceived(RemoteMessage fcmMessage) work very well).
Why the function isn't called and why do I need to send 10 upstream messages to get response from the firebase cloud messaging to my server?
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String sendTo = SENDER_ID + "#gcm.googleapis.com";
RemoteMessage.Builder data = new RemoteMessage.Builder(sendTo);
data.addData("Hello", "World");
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
String messageID = getRandomString();
data.setMessageId(messageID);
Logger.d(TAG, "messageID: " + messageID);
FirebaseMessaging.getInstance().send(data.build());
}
} catch (Exception e) {
Logger.e(TAG, "Error sending upstream message: " + e.getMessage());
return "Error sending upstream message:" + e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Logger.e(TAG, "send message failed: " + result);
}
}
}.execute(null, null, null);
}
Found out the problem!!!
The problem was on the server side.
Every time I send a message to the app (android), I started a new connection to the gcm server, when maintaining a continuous connection it worked great.
For the problem with the onMessageSent not called it was because, before you send the message you need to set time to live (setTtl(Time_in_seconds)) for the message.
RemoteMessage.Builder data = new RemoteMessage.Builder(mSendTo);
data.setMessageId(messageID);
data.setTtl(120);
data.addData("Hello", "World");
FirebaseMessaging.getInstance().send(data.build());
buttonUpstreamEcho.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Echo Upstream message logic");
String message = editTextEcho.getText().toString();
Log.d(TAG, "Message: " + message + ", recipient: " + token);
FirebaseMessaging.getInstance().send(new RemoteMessage.Builder(FCM_PROJECT_SENDER_ID + FCM_SERVER_CONNECTION)
.setMessageId(Integer.toString(RANDOM.nextInt()))
.addData("message", message)
.addData("action", BACKEND_ACTION_ECHO)
.build());
// To send a message to other device through the XMPP Server, you should add the
// receiverId and change the action name to BACKEND_ACTION_MESSAGE in the data
}
});
This is a sample Android project to showcase the Firebase Cloud Messaging (FCM) to manage upstream and downstream messages.
https://github.com/carlosCharz/FCMTest
This is the video in youtube that explains what it does.
https://www.youtube.com/watch?v=SEzOKSoAMG0
Hope you find it useful.
Using the builder pattern - it is always best to chain your calls to the setter methods. So my suggestion, and based on some working examples such as this one here, would be to change your code into something like this (note that I got rid of the for-loop - you can put it back if you need it, I don't see why - perhaps you were testing out?:
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String sendTo = SENDER_ID + "#gcm.googleapis.com";
String messageID = getRandomString();
try {
FirebaseMessaging.getInstance().send(new RemoteMessage.Builder(sendTo)
.setMessageId(messageID)
.addData("my_message", "Hello, World")
.build());
} catch (Exception e) {
Logger.e(TAG, "Error sending upstream message: " + e.getMessage());
return "Error sending upstream message:" + e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Logger.e(TAG, "send message failed: " + result);
}
}
}.execute(null, null, null);
}
I hope this helps - try it out and let me know if it works or what errors you are getting.
I am trying to understand the code behind Google's GCM quickstart example. Specifically, I don't understand how the code checks whether registration is already done.
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
if (checkPlayServices()) {
// Start IntentService to register this application with GCM.
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
}
RegistrationIntentService:
#Override
protected void onHandleIntent(Intent intent)
{
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.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
// 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.
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.
sendRegistrationToServer(token);
// 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(AppSharedPreferences.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(AppSharedPreferences.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);
}
In RegistrationIntentService, the comments say that initially the call goes to retrieve the token, but subsequent calls are local. Does this mean that it will simply check to see if the app already has the token and not make the call anymore? I really don't understand this portion and I don't see anywhere in this example code where it checks for the existence of the token.
For that logic, you can refer to my working sample code:
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private final String SENDER_ID = "425...."; // Project Number at https://console.developers.google.com/project/....
private final String SHARD_PREF = "com.example.gcmclient_preferences";
private final String GCM_TOKEN = "gcmtoken";
private final String LOG_TAG = "GCM";
public static TextView mTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences appPrefs = mContext.getSharedPreferences(SHARD_PREF, Context.MODE_PRIVATE);
String token = appPrefs.getString(GCM_TOKEN, "");
if (token.isEmpty()) {
try {
getGCMToken();
} catch (Exception e) {
e.printStackTrace();
}
}
mTextView = (TextView) findViewById(R.id.textView);
}
private void getGCMToken() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
InstanceID instanceID = InstanceID.getInstance(mContext);
String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
if (token != null && !token.isEmpty()) {
SharedPreferences appPrefs = mContext.getSharedPreferences(SHARD_PREF, Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = appPrefs.edit();
prefsEditor.putString(GCM_TOKEN, token);
prefsEditor.apply();
}
Log.i(LOG_TAG, token);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
}
You can read my answer at the following for more information
Adding Google Cloud Messagin (GCM) for Android - Registration process
Hope this helps!
InstanceID which is part of the Google Play Services library will check if there is a cached token and return that when getToken is called. If there is no cached token then it will go out to the network to retrieve a new token and return that.
Thus your application has to handle the possibility of that calling InstanceID.getToken will result in a network call. Hence the reason it is called in an IntentService.
I'm able to receive a GCM registration id (for push notifications) thanks to this google guide and store the reg.id in a database and without authentication everything works fine.
I use web api 2, oauth 2 authentication and account manager.
1) User sign in to application, App creates an account for the user.
- Or if an account exists auto sign the user in.
2) App gets auth token, if expired retrieves it via Volley string request
3) App checks UserData of Account Manager if reg. id received before. If not App requests an reg. id from GCM and posts it to the server via Volley(AuthToken is required here) and App sets a userdata in account that reg. id has received.
With the above flow of my app which is exists only in my mind at the moment, I've some questions.
First, how can I get auth token first and move to the step 3 which is IntentService according to the Guide.
Second, let's say we managed to do first question. What happens if user login to his account from a different device? Should I update his reg. id for his new device. But what if this device was temporary and he returns to use his permanent device? Notifications will be sent to tempopary device because it was the last device he signed in!
I'm really confused and will be apreciated for anyone who lights my way. Thanks.
Edit
Instead of following Google's guide (instead of using IntentService) is it possible getting both Authorization Token and Registration Id(Token) in a AsyncTask?
Just as answer for 2nd part of question.
Relation between user and his regId should be 1 to n. So 1 user can have multiple devices, and by this multiple regId. And when you would like to send message to this user - you should send multiple messages (1 to every device). Other possible solution is using lately introduced Device Group Messaging, and IMO is preferable way for it.
Thanks for your answers, it only works if I use two async tasks and here is my solution. Any criticism or recommendation will be welcome.
I create a TokenCheckerActivity and put it between login activity and main activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_token_checker);
webApiUri = "url Here";
tokenContext = this;
accountManager = accountManager.get(tokenContext);
PpSharedPreferences ppSharedPreferences = new PpSharedPreferences(tokenContext);
ppAuthenticator = new PpAuthenticator(tokenContext);
account = ppAuthenticator.getCurrentAccount(ppSharedPreferences.getUsername());
new GetAuthorizationToken().execute(ppSharedPreferences.getUsername());
}
Async Tasks
/**
* Gets the authorization token and checks if GCM registration id received.
* Gets reg is if not exists.
*/
private class GetAuthorizationToken extends AsyncTask<String,Void,String>{
#Override
protected String doInBackground(String... params) {
String username = params[0];
String mAuthToken = ppAuthenticator.getPpAuthToken(username);
return mAuthToken;
}
#Override
protected void onPostExecute(String authToken) {
if(!TextUtils.isEmpty(authToken))
{
final String gcmTokenSent = accountManager.getUserData(account, AccountGeneral.GCM_REGISTRATION_ID);
if (gcmTokenSent == null || !gcmTokenSent.equals("true")) {
new GetGcmRegistrationToken().execute(authToken);
} else {
// We have the Gcm Registration Id continue to the main activity
Intent intent = new Intent(tokenContext, MainActivity.class);
startActivity(intent);
finish();
}
}
}
}
private class GetGcmRegistrationToken extends AsyncTask<String,Void,PpTokens>{
#Override
protected PpTokens doInBackground(String... params) {
PpTokens tokens = new PpTokens();
tokens.setAuthToken(params[0]);
try {
if (checkPlayServices()) {
InstanceID instanceID = InstanceID.getInstance(tokenContext);
String regToken = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
tokens.setRegToken(regToken);
}
} catch (IOException e) {
e.printStackTrace();
}
return tokens;
}
#Override
protected void onPostExecute(PpTokens tokens) {
if (!TextUtils.isEmpty(tokens.getRegToken()))
{
sendRegistrationToServer(tokens.getRegToken(),tokens.getAuthToken());
}
}
}
private class PpTokens
{
private String authToken;
private String regToken;
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
public String getRegToken() {
return regToken;
}
public void setRegToken(String regToken) {
this.regToken = regToken;
}
}
Send reg id to server
private void sendRegistrationToServer(String regToken, final String authToken) {
final String tag_json_obj = "json_obj_req";
String url = webApiUri + "?gcmRegistrationToken=" + regToken;
JsonObjectRequest objectRequest = new JsonObjectRequest(Request.Method.POST, url, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Log.d("Name for Reg Token:", response.getString("Name"));
// 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.
accountManager.setUserData(account, AccountGeneral.GCM_REGISTRATION_ID, "true");
Intent intent = new Intent(tokenContext, MainActivity.class);
startActivity(intent);
finish();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
String errorMessage = JsonErrorMessageHandler.onErrorResponse(error);
accountManager.setUserData(acc, AccountGeneral.GCM_REGISTRATION_ID, "false");
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "Bearer " + authToken);
return headers;
}
};
int socketTimeout = 5000;
int maxRetries = 3;
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, maxRetries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
objectRequest.setRetryPolicy(policy);
// Adding request to request queue
AppController.getInstance().addToRequestQueue(objectRequest, tag_json_obj);
}
I have an activity that call an Otto Event called LoadAuthenticateEvent this event then goes to my ClientManager.java where the following code is:
#Subscribe
public void onLoadAuthenticateEvent(LoadAuthenticateEvent loadAuthenticateEvent) {
// GCM cannot register on the main thread
String deviceID = "";
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
String differentId = GCMRegistrationUtil.registerDevice(mContext);
Log.d(TAG, "Device Id: " + differentId);
}
});
thread.start();
String email = loadAuthenticateEvent.getEmail();
String password = loadAuthenticateEvent.getPassword();
Callback<User> callback = new Callback<User>() {
#Override
public void success(User user, Response response) {
sClient.setOrganization(user.getRole().getOrganization().getSubdomain());
mBus.post(new LoadedMeEvent(user));
}
#Override
public void failure(RetrofitError retrofitError) {
mBus.post(new LoadedErrorEvent(retrofitError));
}
};
sClient.authenticate(email, password, deviceID, PLATFORM, callback);
}
The problem is that the server needs the deviceID, but GCM requires that a call be asynchronous and not on the main thread, how should I go about implementing this where I can properly get the deviceID and then pass it to sClient? Since it is possible that deviceID might be empty.
The best way is to contact GCM is through services.
Creates a IntentService to catch the intent released from the
activity
onHandleIntent(Intent intent)
Device sends service request GCM and receives the tokenID.
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Implement this method to send any registration to your app's servers.
sendRegistrationToserver(token)
Notify UI that registrationComplete
Intent registrationComplete = new Intent(GcmUtils.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
(Optional) Subscribe to topic channels
private void subscribeTopics(String token, ArrayList topics_gcm)throws IOException {
for (String topic : topics_gcm) {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, topic, null);
}
}
Full IntentService:
public class RegistrationIntentService extends IntentService {
private static final String TAG = "RegIntentService";
public RegistrationIntentService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
ArrayList<String> topics_gcm = intent.getStringArrayListExtra("topics_gcm");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
// In the (unlikely) event that multiple refresh operations occur simultaneously,
// ensure that they are processed sequentially.
synchronized (TAG) {
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
// [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.
//sendRegistrationToServer(token);
// TODO: Subscribe to topic channels
//subscribeTopics(token, topics_gcm);
// 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(GcmUtils.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(GcmUtils.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(GcmUtils.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
/**
* Persist registration to third-party servers.
*
* Modify this method to associate the user's GCM registration token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
}
/**
* Subscribe to any GCM topics of interest, as defined by the TOPICS constant.
*
* #param token GCM token
* #throws IOException if unable to reach the GCM PubSub service
*/
// [START subscribe_topics]
private void subscribeTopics(String token, ArrayList<String> topics_gcm) throws IOException {
for (String topic : topics_gcm) {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, topic, null);
}
}
// [END subscribe_topics]
}
Start IntentService from any context: activity, service....
Intent intent = new Intent(getContext(), egistrationIntentService.class);
intent.putCharSequenceArrayListExtra("topics_gcm", topcics_gcm);
getContext().startService(intent);
If you want to make the sClient call on the UI thread (not sure if that is a good solution for you), use a Handler to call it once your GCM registration ID is returned by GCM.
The accepted answer here has sample code to help you out.
I ended up using an AsyncTask for this specific thing like so:
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String regId = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(mContext);
}
regId = gcm.register(GCMConfig.getSenderId());
storeRegistrationId(mContext, regId);
} catch (IOException e) {
Log.e(TAG, "Error: ", e);
e.printStackTrace();
}
Log.d(TAG, "GCM AsyncTask completed");
return regId;
}
#Override
protected void onPostExecute(String result) {
// Get the message
Log.d(TAG, "Registered with GCM Result: " + result);
}
}.execute(null, null, null);
}
This worked really well inside the GCMRegistration class
I am working in an android application to implement facebook chat and I have implemented it successfully using asmack libary. But when I tried to sent message when there is no internet connection XMPPException is not caught correctly. It shows Class file editor-source not found. I have downloaded my asmack libary from this link. Please look into my code and suggest me a solution.
Thanks.
public Boolean sentMessage(String message, Long senderid) {
ChatManager chatmanager = connection.getChatManager();
Chat newChat = chatmanager.createChat("-" + senderid
+ "#chat.facebook.com", new MessageListener() {
#Override
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message.getBody());
}
});
try {
newChat.sendMessage(message);
} catch (XMPPException e) {
return false;
}
return true;
}