I am embedding GCM for push notification in my app. I'm facing a very weird problem, on first run I'm not able to get GCM registration token, but when you run my app second time you will get the registration ID printing on the console. I don't know what am I doing worng. Here is what I have done so far.
This is my onCreate() method where I want to print GCM regID:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String regId = GCMRegistrar.getRegistrationId(this);
GCM_regID = regId;
System.out.println("GCM regId: "+GCM_regID);
Doing the following code inside onCreate():
/**
* Google Cloud Messaging - Getting server Url and device ID to send it
* across Google server for the notification..
*/
mGCMReceiver = new GCMReceiver();
mOnRegisteredFilter = new IntentFilter();
mOnRegisteredFilter.addAction(Constants.ACTION_ON_REGISTERED);
if (Constants.SENDER_ID == null) {
// mStatus.setText("Missing SENDER_ID");
return;
}
if (Constants.SERVER_URL == null) {
// mStatus.setText("Missing SERVER_URL");
return;
}
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
if (!regId.equals("")) {
sendIdToServer(regId);
} else {
GCMRegistrar.register(getApplicationContext(), Constants.SENDER_ID);
}
sendIdToServer(regId);
}
Sending GCM_regId to server via these method as guided in one of the tutorial:
/**
* GCM - sending the data in json format to server db
* */
public void sendIdToServer(String regId) {
(new SendRegistrationIdTask(regId)).execute();
GCM_regID = regId;
}
private class GCMReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String regId = intent
.getStringExtra(Constants.FIELD_REGISTRATION_ID);
token = regId;
}
}
private final class SendRegistrationIdTask extends
AsyncTask<String, Void, HttpResponse> {
// private String mRegId;
public SendRegistrationIdTask(String regId) {
// mRegId = regId;
}
#Override
protected HttpResponse doInBackground(String... regIds) {
// String url = Constants.SERVER_URL + "/register";
return null;
}
#Override
protected void onPostExecute(HttpResponse response) {
if (response == null) {
return;
}
StatusLine httpStatus = response.getStatusLine();
if (httpStatus.getStatusCode() != 200) {
Log.e(Constants.TAG, "Status: " + httpStatus.getStatusCode());
return;
}
}
}
I don't think so, GCMIntentService class is needed here for my problem. Please look into this and help me in getting out of this issue.
I'm able to print in GCMIntentService class, on onRegistered(). Here it goes:
#Override
protected void onRegistered(Context context, String regId) {
Intent intent = new Intent(Constants.ACTION_ON_REGISTERED);
intent.putExtra(Constants.FIELD_REGISTRATION_ID, regId);
context.sendBroadcast(intent);
}
I have to print the regId on MainActivity, on onCreate().
Registering the device will take some time.. So if you will try to retrieve the registration id immediately after registering the device in onCreate() then every time it will return a null value.. So try to register your device inside onCreate() and retrieve the id in any different activity/Service (You can retrieve the Id from GCMIntentService class the api from GCM). Note method of GCM Intent Service class.
protected void onRegistered(Context arg0, String arg1) {
Logger.d(arg0, "REG ID="+arg1);
regID = arg1;
}
This is method is called after Reg is done.
So for my case the regID i took this as a static string and am accessing it else.
OR Like u want the regID on Activity B it is preferred to Register it on Activity A and retrieve it via the static string in the above method from the GCMIntent Class.
The reason that you are not getting the regID the first time you run your app is because your app does not have a regID yet, it needs to ask the GCM servers for it. So when you call final String regId = GCMRegistrar.getRegistrationId(this); the first time it will return an empty string. However once you have been registered with GCM the next time you make that call (the next time your app starts) your regID will be returned. The GCM jar simply stores the regID as a string in the app prefs.
When the GCM servers register your device, you will know about it when GCMIntentService.OnRegistered() is called. In your code you are broadcasting an intent with your regID in the GCMIntentService.OnRegistered() which I assume you want to handle with your GCMReceiver (?) although in the code sample you have shown I do not see you creating an instance of the class or registering it as a receiver.
What I think you will want to in GCMReceiver.onReceive() is call sendIdToServer() with the regID that you got from the intent. e.g.
#Override
public void onReceive(Context context, Intent intent) {
String regId = intent
.getStringExtra(Constants.FIELD_REGISTRATION_ID);
Log.i("regID: ", regId);
sendIdToServer(regId);
}
We have a GCM/Airbop client sample that you might want to look at. It is based on the GCM Demo app which you should take a look at.
If you are not logged into a Google account in the emulator will not work. Try logging in with a valid account of Google ( gmail ) and see .
Related
Last year when you added a Cloud Endpoints plus GCM module to an Android Studio project, the IDE created some sample code both in the backend and the app that showed how to use GCM with Cloud Endpoints.
However, with the newer versions of Android Studio you only get the backend part added for you. So I went back into my old projects and dug up some of the convenient app code which registered, and sent GCM push notifications in Android.
Here is what that code looks like:
GcmBroadcastReceiver.java
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
GcmIntentService.java
public class GcmIntentService extends IntentService {
android.support.v4.app.NotificationCompat.Builder notification;
public GcmIntentService() {
super("GcmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
if (extras != null && !extras.isEmpty()) { // has effect of unparcelling Bundle
// Since we're not using two way messaging, this is all we really to check for
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
Logger.getLogger("GCM_RECEIVED").log(Level.INFO, extras.toString());
showToast(extras.getString("message"));
sendNotification(extras.getString("message"));
}
}
//call to the API and get new data.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
protected void showToast(final String message) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
});
}
private void sendNotification(String msg) {
notification = new android.support.v4.app.NotificationCompat.Builder(this);
//set number of notifications count
//notification.setNumber(x);
//cancels notification when app is opened.
notification.setAutoCancel(true);
//build the notification
notification.setSmallIcon(R.drawable.greenicon);
notification.setTicker("This is the ticker!");
//set time
notification.setWhen(System.currentTimeMillis());
notification.setContentTitle("New message!");
notification.setContentText(msg);
notification.setSound((Settings.System.DEFAULT_NOTIFICATION_URI));
//LED
notification.setLights(Color.RED, 3000, 3000);
// intent
Intent intent = new Intent(this, MainActivity.class);
//give phone access to perform this intent b/c they may be in another part of their phone.
//aka gives phone access to the intents in our app
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//what to do when notification is clicked:
notification.setContentIntent(pendingIntent);
//Builds notification and issues it (sends it to device). Can build and send out notifcations
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//send out notification with uniqueID
nm.notify(2158, notification.build());
}
}
GcmRegistrationAsyncTask
class GcmRegistrationAsyncTask extends AsyncTask<Void, Void, String> {
private static Registration regService = null;
private GoogleCloudMessaging gcm;
private Context context;
// TODO: change to your own sender ID to Google Developers Console project number, as per instructions above
private static final String SENDER_ID = "1026567774990";
public GcmRegistrationAsyncTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(Void... params) {
if (regService == null) {
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// Need setRootUrl and setGoogleClientRequestInitializer only for local testing,
// otherwise they can be skipped
.setRootUrl("https://push-notif-45657747.appspot.com/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest)
throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
}) ;
// end of optional local run code
regService = builder.build();
}
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
String regId = gcm.register(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.
// The request to your server should be authenticated if your app
// is using accounts.
regService.register(regId).execute();
} catch (IOException ex) {
ex.printStackTrace();
msg = "Error: " + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Logger.getLogger("REGISTRATION").log(Level.INFO, msg);
}
}
However, I am getting some deprecated errors in Android Studio now:
gcm.register(SENDER_ID); is deprecated and so is GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.
This GCM stuff is pretty confusing to begin with and while there is some information here on how to use it, I was wondering if anyone had any currently working non-deprecated examples or maybe you could suggest some edits to the above code if you know what you are doing...? Much thanks!
Wanted to give people a little guide here in case they were lost.
First check out and stay up to date with this Google Cloud Messaging Android example:
https://github.com/google/gcm
To make it work you will have to generate a google-services.json file which you can do here:
https://developers.google.com/mobile/add
Make sure you are logged into the google developers console before you go that link. It will load your projects for you and automatically set up the gcm api key for you in your projects credentials.
Copy/paste the google-services.json into the /app directory of your Android project.
Add a cloud endpoints with gcm module to the android project.
Enter your gcm api key (which you can view on your credentials page on developers console) into the webapp-WEB_INF/appengine-web.xml file in your cloud endpoints backend:
<property name="gcm.api.key" value="your-api-key-here"/>
This way, inside the Android client and MessagingEndpoint the code will automatically get the api key (in the endpoint it will be the line Sender sender = new Sender(API_KEY); for example, which will just retrieve it for you).
Run the sample gcm android project and it should work. Send a push notification with the API's explorer you deployed.
BIG NOTE: when you are ready to use the sample code in your own app make sure the RegistrationIntentService is in the root of your package or it won't work! Took a while to figure that out... Not sure if it is a bug or what.
I am successful in registering to GCM server and posting registration id to our server but it is taking two attempts to complete the whole cycle. I need to do all these in single attempt. I am adding my code below please correct me if I am wrong.
First attempt: It will register to gcm server.
Second attempt : It will register to our server
protected void RegisterApp() {
// -----------------------------------
aServerUtility = (GCMServerController) getApplicationContext();
// Check if Internet present
if (!aServerUtility.isConnectingToInternet()) {
// Internet Connection is not present
aServerUtility.showAlertDialog(this, "Internet Connection Error",
"Please connect to Internet connection", false);
// stop executing code by return
return;
}//
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest permissions was properly set
GCMRegistrar.checkManifest(this);
// Register custom Broadcast receiver to show messages on activity
registerReceiver(mHandleMessageReceiver, new IntentFilter(Config.DISPLAY_MESSAGE_ACTION));
// Get GCM registration id
final String regId = GCMRegistrar.getRegistrationId(this);
final String Username = etUserName.getText().toString();
if (regId.equals("")) {
// Register with GCM
GCMRegistrar.register(common_signin.this, Config.GOOGLE_SENDER_ID);
} else {
// Device is already registered on GCM Server
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
Toast.makeText(getApplicationContext(),
regId + " - Already registered with GCM Server",
Toast.LENGTH_LONG).show();
Log.i(TAG, "Registered device (regId = " + regId + ")");
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// Register on our server
// On server creates a new user
try {
aServerUtility.register(context, Username, "", regId);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
// execute AsyncTask
mRegisterTask.execute(null, null, null);
}
}
// ---------------------------------------
// Create a broadcast receiver to get message and show on screen
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(
Config.EXTRA_MESSAGE);
// Waking up mobile if it is sleeping
aServerUtility.acquireWakeLock(getApplicationContext());
// Display message on the screen
// lblMessage.append(newMessage + "");
Toast.makeText(getApplicationContext(),
"Got Message: " + newMessage, Toast.LENGTH_LONG).show();
// Releasing wake lock
aServerUtility.releaseWakeLock();
}
};
}
Two calls to this method were needed because you only called GCMRegistrar.register() when there is no existing registration ID (in if (regId.equals(""))). The method ends after that and does not call aServerUtility.register(), which performs the server registration. It is only in the second call, when if (regId.equals("")) is false, that you actually make the call to register with your server.
Having said all this, please take note that GCMRegistrar is deprecated. I would suggest moving to GCM.
On server side, i found for one device there multiple registration IDs, which obviously is creating for me lots of issues. like messages received many times.
How could I get red of old registration IDs, or make sure that registration doesn't happen if there a valid registration ID.
I follow the example tutorial on Android doc when I wrote my app as below:
checkNotNull(SERVER_URL, "SERVER_URL");
checkNotNull(SENDER_ID, "SENDER_ID");
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
// NOT required any more GCMRegistrar.checkManifest(this);
/**
* this code to register reciver moved to message actvity
*/
//registerReceiver(mHandleMessageReceiver, new IntentFilter(
// DISPLAY_MESSAGE_ACTION));
/* final String */regId = GCMRegistrar.getRegistrationId(this);
/**
* save regId in pref to be used by Location update service
*/
SavePreferences("regId", regId);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
;;
// Skips registration.
// -- mDisplay.append(getString(R.string.already_registered) +
// "\n");
// System.out.println(getString(R.string.already_registered)
// + "\n");
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
boolean registered = ServerUtilities.register(context,
regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
Please refer link: http://stackoverflow.com/questions/15030233/gcm-multiple-application-id-in-server-side/15030465#comment21123039_15030465
Also check canonical registration ids concept :http://developer.android.com/google/gcm/adv.html
or
when a new id is generated you will try to register it GCM registrar. when this new registration id successfully register your onRegister() method of service will be called .
here you can add code to replace previous one with new one.but for this along with registration id you need to send some unique reference to server,so for next time you can check this unique reference and replace existing registration id with new one
checked following code:
#Override
protected void onRegistered(Context context, String registrationId) {
ServiceUtilities.register(context, registrationId);
//call service and replace id
}
I want to implement push notification in Android using Phonegap. I have successfully created an app using https://github.com/marknutter/GCM-Cordova.
I have also created my app id and sender id using https://code.google.com/apis/console/.
Can anyone suggest where I should put these keys in my project?
I also use the plugin.
Not sure about the app id, but the send ID you pass in the function for registering:
GCM.register(STRING_YOUR_SENDER_ID, "GCM_Event", GCM_Success, GCM_Fail );
Check the GCM documentation here, here is a code snippet (I modified a bit from the original example in the documentation):
public class GCMIntentService extends GCMBaseIntentService {
private static final String SENDER_ID = ""; // Your project ID from the API Console
public GCMIntentService() {
super(SENDER_ID);
}
#Override
public void onCreate() {
super.onCreate();
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
} else {
Log.v(TAG, "Already registered");
}
}
#Override
protected void onError(Context context, String error) {
}
#Override
protected void onMessage(Context context, Intent message) {
//String value = message.getExtras().getString("message");
}
#Override
protected void onRegistered(Context context, String resId) {
// You should save the resId to use it when sending a message from your server
}
#Override
protected void onUnregistered(Context arg0, String msg) {
// Delete the resId from your server
}
}
To test it you need first to call the above service to register your device with GCM service (and get a registration id that you will use when sending the message), you can do something like:
Intent registrationIntent = new Intent(
"com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app",
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
// registrationIntent.putExtra("sender", "Your sender id"); // Better than keep the sender id hard coded in the service
context.startService(registrationIntent);
To send a message you can have a simple Java application as below (you can do it from other languages as well):
public static void sendMessage(String msg) throws IOException {
String myApiKey = "Your Browser API Key";
String regId = "Registeration id"; // the value you received in
// onRegistered() in the above
// onRegistered class
Sender sender = new Sender(myApiKey);
Message message = new Message.Builder().addData("message", msg).build();
Result result = sender.send(message, regId, 5); // 5 is the maximum number of trials to deliver your message
if (result.getMessageId() != null) {
System.out.println("Message sent");
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// This means that the registration id got updated, so use the new one for future messages
System.out.println("canonicalRegId: " + canonicalRegId);
}
} else {
System.out.println("error: " + result.getErrorCodeName());
}
}
use the plugin
cordova plugin add phonegap-plugin-push --variable SENDER_ID="XXXXXXX"
replace the xxxxxx with your sender id
sender id is the project id/project number in google console
in your javascript add the following code for registering
var push = PushNotification.init({
android: {
senderID: "XXXXXXXX" //add your sender id here
},
ios: {
alert: "true",
badge: "true",
sound: "true"
},
windows: {}
});
push.on('registration', function(data) {
consol.log(data.registrationId); //this function give registration id from the GCM server if you dont want to see it please comment it
document.getElementById("gcm_id").value= data.registrationId; //showing registration id in our app. If it shows our registration process is suscess
//$("#gcm_id").val(data.registrationId); if you are using jquery
});
if you want to get more detail in how to implement push notification in cordova please go through the following link
http://phonegaptut.com/2016/05/31/how-to-send-push-notifications-in-phonegap-application/
I´m refering to thestandard Android GCM Tutorial
it seems like on first app launch the registration ID is set after the oncreate() Method has fired. How can I send the Registration ID from the GCMRegistrar.java back to the activity?
I call this in my MainActivity`s onCreate():
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
String regId = GCMRegistrar.getRegistrationId(this);
//provide RegID for the Class that can communicate with Javascript.
Log.v(TAG, "regID: "+regId);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
} else {
Log.v(TAG, "Already registered");
}
EDIT:
//Call after .register also returns empty String on first App Launch
String regId2 = GCMRegistrar.getRegistrationId(this);// returns ""
It returns an empty String here on first start. In the log I see the registration ID is set later in the GCMRegistrar.java
static String setRegistrationId(Context context, String regId) {
Log.v(TAG, "REGISTRATION ID IN SET: "+regId);
final SharedPreferences prefs = getGCMPreferences(context);
String oldRegistrationId = prefs.getString(PROPERTY_REG_ID, "");
int appVersion = getAppVersion(context);
Log.v(TAG, "Saving regId on app version " + appVersion);
Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.commit();
return oldRegistrationId;
}
This Method above is called in the GCMBaseIntentService.
If I run the App a second time, I can get the Reg. ID in the Mainactivity, how can I implement something like a callback function so I can access the Reg. ID in the MainActivity?
Yes GCMRegistrar.getRegistrationId(this) will return empty string if you are not registered with GCM. Once you register then it will return actual registration ID. So that They used if() condition in their demo to check Whether this device is registered or not
Create an interface say "GCMInterface" with an abstract method with an string argument, let your activity implement this interface i.e whatever you want to do with regId. Pass the reference of your activity to your GCMIntentService. When onREgistered method of GCMIntentservice is called, call activity.method and pass regId as argument.
public interface GCMInterface {
public void mOnRegistered(String id);
}
...
public class MyActivity implements GCMInterface {
// implementation of GCMInterface interaface
public void mOnRegistered(String id) {
// do whatever you want to do with id
}
}
...
public class GCMIntentService extends GCMBaseIntentService {
GCMInterface interface; // initialize it with your activity's context
public GCMIntentService() {
}
#Override
protected void onRegistered(Context context, String regId) {
// call mOnRegistered method with your activity's context like
interface = (GCMInterface)context;
if(interface != null) {
interface.mOnRegistered(regId);
}
}
}
I hope you got my point.