The basic architecture of the push functionality that I am trying to create is
1) I would get the phone id and store it in my db
2) Only when something happened on the server I would try to create a push message
So I do not need the part where the app tries to call the server to send it the message. I am wondering what should I extend in the Service class and what methods in it do I need to implement.
Also, am I able to get the device id and send that info to my server without using the server?
I am using Google cloud messaging.
Thanks!
Ok, to get the registration ID for you phone you will need to do the following in an activity:
import com.google.android.gcm.GCMRegistrar;
private final static String SENDER_ID = "0001234567" // API Key, see comments;
public class myActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState)
{
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals(""))
{
GCMRegistrar.register(this, SENDER_ID);
Log.i(TAG,"registered a new ID");
}
else
{
Log.i(TAG,"Already Registered");
}
your google reg will be contained in regId
You will need this in your Manifest:
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="my.namespace" />
</intent-filter>
</receiver>
where my.namespace will equal whatever your package name is
You can follow this tutorial.. it uses the new GooglecloudMessaging API. And yes, you should extend IntentService cause the other ones deprecated now.
Related
I am struggling to find the cause for the duplicate notification message on android using Firebase, Unity and sending from nodejs with the help of node-gcm.
Here is all relative code:
Unity3d client side I got:
public class PushServiceManager : MonoBehaviour
{
public string Identifier;
public string SkuId;
public SQSParameters sqsOptions;
private SQSManager sqs;
// Use this for initialization
void Start()
{
sqs = new SQSManager(this.sqsOptions);
sqs.Initialize(this.gameObject);
Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
}
public void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
{
Debug.Log("Received Registration Token: " + token.Token);
RegistrationToken t = new RegistrationToken();
t.id_token = token.Token;
// sends token to node server
sqs.SendRegistrationToken(t);
}
public void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
{
Debug.Log(e.Message.Data.ToString());
}
}
Nodejs server side:
pushNotification(pushRequest, tokens, callback) {
// This is an array of firebase tokens
let registrationTokens = tokens.map(function (token) {
return token.token;
});
let message = new gcm.Message({
timeToLive: pushRequest.ttl
});
let note = {
title: 'Message title',
icon: 'none',
sound: 'none',
body: 'message text'
};
message.addNotification(note);
let payload = {
data: {foo: "foo"}
}
message.addData(payload);
this.sender.send(message, {registrationTokens}, callback);
}
Result on device is:
Question:
Why do I get the second empty push notification?
Tapping first one opens up a unity game as expected but tapping that empty one does not.
Where am I going wrong with this?
Thank you!
01.31.17 UPDATE:
Shutout to firebase support team for looking into this issue...
I have the same duplicate notifications issue in my project and below is the solution.
Check your generated AndroidManifest.xml by unity build in [Your unity project folder]\Temp\StagingArea
Look for all receivers that have below line:
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
In my case, I have below that added by the Amazon AWS unity plugin
<receiver android:name="com.amazonaws.unity.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.amazonaws.unity" />
</intent-filter>
</receiver>
and below from Firebase plugin
<receiver android:exported="true" android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.bbbbbttttttgggg.sssssstttt" />
</intent-filter>
</receiver>
The duplicated notification is caused by amazon AWS GCMBroadcastReceiver.
There are two options to fix this.
Remove the receiver if you do not need it.
Add android:exported="false" to the receiver.
Example:
<receiver android:exported="false" android:name="com.amazonaws.unity.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.amazonaws.unity" />
</intent-filter>
</receiver>
Remember to make the change in the original AndroidManifest.xml that contains the receiver. Not the one in Temp\StagingArea. It should be one of the AndroidManifest.xml file inside [Your unity project folder]\Assets\Plugins\Android or it sub folder.
I am facing the same problem with firebase. I am using Firebase together with Microsoft Azure Notification Hub. If I send a test message via Microsoft Notification Hub I only get one message and that message is handled by my Push Handler. Otherwise if I send push notifications through firebase console I am getting two message like you(also one is empty). One message is handled by my push service and the second one is somehow handled by "no code"! I cannot find in my code where the push notification send by firebase is handled? There must be a OnReceiveMessage method in the background which handles the notification. For me I come with following solution: I just uncommented my push handler and now I am only getting one notification, but this without sound...somehow I have to override that function and implement sound behaviour. Have you found a solution for that? The empty message is caused by your push handler the second full message is handled in the background by firebase.
I also getting two different types of message in my onReceive() method:
Microsoft: Bundle[{google.sent_time=1484748187461, from=230XXXXXXXXXX, google.message_id=0:1484748187469719%8a1048bff9fd7ecd, message=Test through Microsoft, collapse_key=do_not_collapse}]
Firebase: Bundle[{google.c.a.udt=0, google.sent_time=1484748603664, gcm.notification.e=1, google.c.a.c_id=3001008833206773803, google.c.a.ts=1484748604, gcm.n.e=1, from=230XXXXXXXXXXX, google.message_id=0:1484748603943016%8a1048bf8a1048bf, gcm.notification.body=Test through firebase, google.c.a.e=1, collapse_key=com.sub_zero.ipostcard}]
So the message received through firebase is handled one time in the background somewhere maybe in through the firebase lib and the second time I suppose by your handler. That why you getting two of them...Just uncomment your push handler and test it again.
I found what was cause this issue...
I had AWS module included into the project which had it's own manifest file. In it there was a receiver declaration for "GCMBroadcastReceiver" in combination with service declaration for "GCMIntentService".
Removing those solved the issue
The AndroidManifest.xml:
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Receiver for GCM Messages-->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<action android:name="com.google.android.c2dm.intent.GCM_RECEIVED_ACTION"/>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="de.comp.module.client" />
</intent-filter>
</receiver>
<service
android:name="de.comp.module.client.gcm.GCMBroadcastIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
The GCMBroadcastIntentService class:
public class GCMBroadcastIntentService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle extras) {
if (extras.containsKey("message")) {}
}
}
The onMessageReceived() method is never called. The server works fine, the messages are send to Google GCM without any errors, but they never arrive at the device. I also use google-services.json which is stored in the folder /app. In my app the user can loggin after a new installation. In this case a new GCM token is requested and send to the server. So it should be correct and up to date.
What is missing? Thanks for your help.
EDIT:
public class GCMRegistrationIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
try {
// register server key to GCM
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String regid = gcm.register(getString(R.string.gcm_defaultSenderId));
// Request new token for this server key
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Subscribe to topic channels
subscribeTopics(token);
...
}
you must register the sender id in the Gcm:
GoogleCloudMessaging gcm=null;
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(getContext());
}
String regid = gcm.register(SENDER_ID);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
note that SENDER_ID is the id of the server that sends the notification
Use FCM instead of GCM.Check this official links. FCM is a new Version of GCM
Try to start from the beginning.
Make sure SENDER_ID matches the project number in console.
Make sure you implement registration IntentService, device registration is successful and you receive push token (GCM registration id).
Make sure you pass it to server.
Not forget to implement InstanceIDListenerService.
Add missing services to manifest.
Start everything from the beginning and pay attention at each step
https://developers.google.com/cloud-messaging/android/client
Set up the GCM client according to https://developers.google.com/cloud-messaging/android/client
If you still not get a call to onMessageReceived(), you can check whether the problem is somwhat similar to GcmListenerService.onMessageReceived() not called
Also GCM requires a Google account for Pre-4.0.4 devices if you are using the deprecated library (GCMRegistrar.register), . If yes you need to make an entry in AndroidManifest as
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
Refer GET_ACCOUNTS permission while using GCM - Why is this needed?
I inherited an application that already implemented GCM services and is working well enough.
I'm saying well enough because half the times, when the app launches, I get the error INVALID_SENDER and the other half I don't get it!
There is no difference between the times I get the error and the times I don't (or maybe I'm missing the difference).
I do get messages from GCM from time to time (when I don't get the INVALID_SENDER after logging in)
This is the registration code in the onCreate() of my main activity
private void registerGCM() throws Exception {
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
registerReceiver(mHandleMessageReceiver, new IntentFilter(
CommonUtilities.DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, CommonUtilities.SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
ServerUtilities.register(mContext, 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) {
boolean registered = ServerUtilities.register(context, regId);
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
My Manifest file
<receiver
android:name="com.mixpanel.android.mpmetrics.GCMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.says.broadcaster" />
</intent-filter>
</receiver>
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.says.broadcaster" />
</intent-filter>
</receiver>
The reason I have two receivers is that I get push notifications from the stats tracking API I'm using too, and they use GCM.
My app has a new version every week or so, and I was reading that I need to register for a new ID after an app update. Could this be the issue?
Related questions:
Getting INVALID_SENDER on one device while its working with another GCM android
The Broadcasts sent by GCM seem to be ordered. This means they are delivered one at a time. It's possible that the receiver of the library sometimes gets called before your receiver, and it might prevent your receiver from being called (if it cancels the broadcast).
There are several ways to handle it. One way is to give your receiver a higher priority than the library's receiver. Just add the android:priority attribute to the intent-filter of both receiver, and give your receiver a higher priority.
Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.
When your broadcast receiver gets called, you should check that intent.getExtras ().get("from") contains the sender ID you are using. Only in this case you should handle the broadcast. If it's a different sender ID, it's probably the sender ID used by the library, and you should let the library handle it.
If that doesn't solve the problem, you can consider declaring only your receiver in the manifest, and forwarding the GCM broadcast to the other receiver when necessary.
I have created a sample project in Google Console. Referring the sample from this site, http://code.google.com/p/blog-samples/downloads/detail?name=gcm_sample_update1.rar&can=2&q=#makechanges I got my registration Id. Now I don't know what to do next, how I can implement push notification in my app. I want to use Urban Airship to send push to my app. I have created a app in Urban Airship, by giving the package name, API key obtained through Google console. To my server I need to send the Apid to get the messages. But i don't know how to get the Apid.
It is explained here: http://developer.android.com/guide/google/gcm/gs.html
Writing the Android Application
You have to download and add gcm libraries.
Then make changes in manifest:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="xx"/>
and
<permission android:name="my_app_package.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="my_app_package.permission.C2D_MESSAGE" />
inserting Your package name in "my_app_package".
Add other permissions
<!-- App receives GCM messages. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- GCM connects to Google Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
receiver
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="my_app_package" />
</intent-filter>
</receiver>
Again write Your package name in "my_app_package".
Then declare your service
<service android:name=".GCMIntentService" />
You have to create a class named GCMIntentService. This class is going to handle the push notifications.
It should look like this:
class GCMIntentService extends com.google.android.gcm.GCMBaseIntentService{
public void onRegistered(Context context, String regId){}
public void onUnregistered(Context context, String regId){}
public void onError(Context context, String errorId){}
public void onMessage(Context context, Intent intent){}
}
In onMessage() you will have to handle the push message. You can use notifications.
Then in Your starting Activity you will have to put this
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");
}
in onCreate().
SENDER_ID is the String containing all numbers that you got from the google console.
Well.. There are 3 steps to get APID
1. Run the application
2. Open logcat
3. Find the APID
Note first time you may get APID(Android Push ID) null so run it again you will get
If you want to use Urbanairship you have to use their SDK for user registration (look here), cause UA create a new id for each device and not using Google registration Id when pushing message using their API.
Not sure why they doing it like that but that it... =\
Although I try other answer about this problem, it can't be solved.
Sender ID is correct, permissions are added, API key is true.
I use this post for creating the project:
http://developer.android.com/guide/google/gcm/gs.html#server-app
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
regId = GCMRegistrar.getRegistrationId(this);
} else {
Log.v(TAG, "Already registered");
}
String did = getDeviceID();
it returns empty string.
Do you have GCMIntentService defined correctly at the root package of your app?
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="my_app_package" />
</intent-filter>
</receiver>
Make sure that "my_app_package" is identical to the main package of your app, or your id will return an empty string.
Also notice that
GCMRegistrar.register(this, "...");
is asynchronous. Therefore, calling GCMRegistrar.getRegistrationId(this) immediately after that is not reliable, so you should avoid it.
The id will arrive via broadcast callback to your GCMIntentService, as a part of the registration procedure. from there you can then store the gcm/fcm key anywhere you like, and use it in other parts of the application (usually on the server).
If your application launches first time, you have to wait for OnRegistered callback on your GCMIntentService.java file.
GCMRegistrar.register(this, SENDER_ID);
// Android does NOT wait for registration ends and executes immediately line below
// So your regId will be empty.
regId = GCMRegistrar.getRegistrationId(this);
GCMIntentService.java:
#Override
protected void onRegistered(Context c, String regId) {
// You can get it here!
}
Edit: Newer version of GCM library (which bundled with google play services library) waits for response and returns the Registration ID. So this answer is for older GCM libraries.
I have same problem after 1 day struggle I find solution. First i Put my GCM Related Code in To My Main Package and make change according to My Package in androidManifest.xml
i give you simple example. My project name is GCMExample and i have Three package 1. com.examle.GCMExample(Main Package) , 2. com.examle.GCMExample.activity(Second Package) , 3. com.example.GCMExample.adapter(Third Package)
I am not Getting Registration Id When My GCM Related Class File Into My Second Package
My GCM Related class like 1. GCMIntentService , 2. ServerUtilities , 3. CommonUtilities 4. WakeLocker put Into My Main Package com.example.GCMExample
also my androidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<permission android:name="com.example.GCMExample.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.GCMExample.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.example.GCMExample" />
</intent-filter>
</receiver>
<service android:name="com.example.GCMExample.GCMIntentService" />
Registering app com.example.ilkgcm of senders "my_sender_id" -- This error suggests you haven't changed your 'my_sender_id' to a valid sender id which would be a 12-letter code.
If you are not getting response from the registration.one of the main reason is your manifest file is not configured correctly...especially give the "package_name"(your app package name like com.xxx.yyy) in the and correctly.
For example your receiver class name is : "MyCustomIntentService.java" , just change this name as "GCMIntentService.java" then try it again. This is simple solution. Just change your file name on the SRC area.