I am trying to add notifications to my Android app using GCM. I started out by following this
tutorial and using this example project as a reference.
I followed all the steps and created all needed classes and services, but I am still not receiving any notifications.
I'm checking for Google Play Services and seem to register my device successfully on my server. I added the google-services.json file to my project and added all permissions and libraries to the project.
What is going wrong?
Did I forget anything? Do I still need to do something? I'm very confused at this point.
AndroidManifest
Permissions
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="com.manager_android.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="com.manager_android.permission.C2D_MESSAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Receiver and services
<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.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.manager_android" />
<!--here-->
</intent-filter>
</receiver>
<service
android:name=".Activities.GcmIntentService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service
android:name=".Activities.TokenRefreshListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
<service android:name=".Activities.RegistrationService"
android:exported="false">
</service>
Added to app build.gradle
compile 'com.google.android.gms:play-services-gcm:11.0.4'
apply plugin: 'com.google.gms.google-services'
Added to project build.gradle
classpath 'com.google.gms:google-services:3.1.0'
RegistrationService
public class RegistrationService extends IntentService {
public RegistrationService() {
super("");
}
private final String TAG = "RegistrationInfo";
#Override
protected void onHandleIntent(#Nullable Intent intent) {
registerDevice();
}
private void registerDevice() {
InstanceID myID = InstanceID.getInstance(getApplicationContext());
String registratonToken = "";
try {
registratonToken = myID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
log.d("Registration Token", registratonToken);
Map<String, String> params = new HashMap<>();
params.put("user_id", registratonToken);
params.put("platform", "GCM");
SaveSharedPreference.setToken(getApplicationContext(), registratonToken);
//Register token on server
Communication.post(getString(R.string.register_device_url), new RequestParams(params), new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Log.i(TAG, "Registration was successful");
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Log.i(TAG, "Registration failed.");
}
});
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "Couldn't register device", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
TokenRefreshListenerService
public class TokenRefreshListenerService extends InstanceIDListenerService {
#Override
public void onTokenRefresh() {
Intent i = new Intent(this, RegistrationService.class);
startService(i);
}
}
GCMIntentService
public class GcmIntentService extends GcmListenerService {
private int testId = 1234;
#Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("message");
sendNotification(message);
}
private void sendNotification(String message) {
Intent intent = new Intent(this, MainScreenActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), testId, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.logo)
.setContentTitle("GCM Message")
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis());
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(testId, notificationBuilder.build());
}
}
I start the RegistrationService as a service when the user logs in to register the device.
This documentation can help you understand about notifications.
A notification is a message you display to the user outside of your
app's normal UI. When you tell the system to issue a notification, it
first appears as an icon in the notification area. To see the details
of the notification, the user opens the notification drawer. Both the
notification area and the notification drawer are system-controlled
areas that the user can view at any time.
It also provides sample codes on every topic that you will tackle.
For GCM, you can refer to this documentation.
Google Cloud Messaging (GCM) is a free service that enables developers
to send messages between servers and client apps. This includes
downstream messages from servers to client apps, and upstream messages
from client apps to servers.
It will help you understand about the concept of GCM. Also it provides sample to start with.
Related
I’m new to android.I programmed an app that uses Firebase to get push notifications. If I send a notification from the Firebase interface, the device receives the notification only when the app is running. Is there a way to get the notifications when the app is not running or at least to get all the unseen notifications when the app starts up?
Thank you in advance!
I add my code...
This is the class extending the FirebaseMessagingService:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
public MyFirebaseMessagingService() {
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
sendNotification(remoteMessage.getNotification().getBody());
}
private void sendNotification(String messageBody)
{
//Intent intent = new Intent (this, MainActivity.class);
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
notificationBuilder.setContentTitle("My app");
notificationBuilder.setContentText(messageBody);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSound(defaultSoundUri);
//notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0,notificationBuilder.build());
}
}
and this is the class extending the FirebaseInstanceIdService:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService{
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token)
{
}
}
Finally, this is the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="user.pushnotificationexample">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".MyFirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>
This is Xamarin Android solution, but it should get you close to native.
Try test sending a "data" push notification from an online tester like "pushwatch". Make sure to click the json checkbox and add a json payload that includes the key, "data":
{"data": {"title":"This is a title!","body":"This is the body text!"}}
The OnMessageReceived(RemoteMessage message) method, message parameter, has a dictionary property "Data": i.e. message.Data. You'll find the json payload key value pairs (title & body) in that dictionary.
Note, if the app is STOPPED, the user will OPEN the notification and Android will open the app, targeting the Activity used in the pending intent created and used to build the local notification (see below). You will need to handle the notification intent from the OnCreate method of your target Activity ( otherwise if the app is running, you'll handle the intent from an override of OnNewIntent).
public class MainActivity....
{
protected override void OnCreate(Bundle bundle)
{
...
var pushExtra = Intent.GetStringExtra("your.pushextra.key");
if(pushExtra != null)
{
MyType myType = JsonConvert.Deserialize<MyType>(pushExtra);
//if you have a framework like MVVM or startup process
//and your type
//is used for app navigation, you may need to store
//the string value in shared preferences and deserialize
//and use it later
//after the app has completely finished starting
}
}
}
Save yourself some effort, if you need a type or dictionary here, make sure to serialize the object when putting the extra (when you create the local notification) and then deserialize the pushExtra to your concrete type to use an instance of it above. You may find most examples put a string, or int or dictionary of key value pairs.
void ConfigureLocalNotification(MyType typeInstance)
{
...
var intent = new Intent(context, typeof(MainActivity));
var jsonData = JsonConvert.SerializeObject(typeInstance);
Log.Info(TAG, $"Adding IntentExtra: {jsonData}");
intent.PutExtra("your.pushextra.key", jsonData);
intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop |
ActivityFlags.IncludeStoppedPackages); //| ActivityFlags.NewTask);
var pendingIntent = PendingIntent.GetActivity(context, 0, intent,
PendingIntentFlags.OneShot);
....
NotificationCompat.Builder builder = null;
builder = new NotificationCompat.Builder(context ...);
builder.SetContentIntent(pendingIntent) ....
}
Hope this helps
I can receive push notification when my app is in foreground or background
I added FcmBroadcastReceiver extends WakefulBroadcastReceiver in order to get push
when I close my application.
But I continue without receiving notifications when the app is closed.
Thanks for your help.
manifest.xml
<pre>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="<package>.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.package.name.permission.C2D_MESSAGE" />
<receiver
android:name="<package>.Notification.FcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="<package>" />
</intent-filter>
</receiver>
<service android:name=".Notification.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
public class FcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("receiver","okey");
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
MyFirebaseMessagingService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
public class MyFirebaseMessagingService extends FirebaseMessagingService {
Intent i;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
System.out.println("******************** + getMessageType "+remoteMessage.getMessageType());
System.out.println("******************** + getFrom "+remoteMessage.getFrom());
System.out.println("******************** + getMessageId "+remoteMessage.getMessageId());
System.out.println("******************** + getTo "+remoteMessage.getTo());
System.out.println("******************** + getData "+remoteMessage.getData());
System.out.println("******************** + getData().get(message) "+remoteMessage.getData().get("message"));
System.out.println("******************** + getData().get(title) "+remoteMessage.getData().get("title"));
showNotification(remoteMessage.getData().get("type"),remoteMessage.getNotification().getTitle(),remoteMessage.getNotification().getBody());
}
private void showNotification(String to , String title, String message) {
i = new Intent(this,ProfileActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,i, PendingIntent.FLAG_UPDATE_CURRENT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setAutoCancel(true)
.setContentTitle(title)
.setContentText(message)
.setSound(defaultSoundUri)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0,builder.build());
}
}
<code>
The issue that I can see in your code is that you are implementing the FireBase push notification but you are trying to implement that in a way that was used to implement GCM. You don't need to set any Broadcast Receivers, Firebase is doing that by itself. You just have to run "FirebaseMessagingService". The services looks like this:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FCM Service";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO: Handle FCM messages here.
// If the application is in the foreground handle both data and notification messages here.
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated.
Log.d(TAG, "From: " + remoteMessage.getFrom());
Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
}
}
As you can see it has it's own "onMessageReceived" method that takes care of Broadcast Receiver's functionality.
Check this tutorial, it will help you to understand:
https://www.codementor.io/flame3/send-push-notifications-to-android-with-firebase-du10860kb
Note: If you want your app to work in each situation even app is in background, foreground or even closed then you need to send some data through payload in your notification.then it will work for all conditions. To do that you need your own server, Firebase doesn't facilitate for that.
My GCM Service is not working. I have declared a few things in my manifest file which looks something like:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<permission android:name="com.xxxx.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.xxxx.xxxxxx.permission.C2D_MESSAGE" />
<receiver android:name=".core.push.receiver.GCMBroadcastReceiver" android:exported="true" 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.SEND" />
<category android:name="com.xxxx.xxxxxx" />
</intent-filter>
</receiver>
<service android:name=".core.push.service.GCMIntentService" />
My Broadcast receiver code looks like:
public class GCMBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
ComponentName messageProcessingService = new ComponentName(context.getPackageName(), GCMIntentService.class.getName());
context.startService(intent.setComponent(messageProcessingService));
setResultCode(Activity.RESULT_OK);
}
}
My Intent Service:
public class GCMIntentService extends IntentService
{
private static final int NOTIFICATION_NEW_MESSAGE = 0;
public GCMIntentService()
{
super(GCMIntentService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent)
{
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty())
{
if (!GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
return;
}
// TODO Make more efficient. Load message stream here, not in two places later!
int newUnreadCount = MessagingController.getInstance().getUnreadCount() + 1;
MessagingController.getInstance().prepareStream();
MessagingController.getInstance().setUnreadCount(newUnreadCount);
final boolean isUserAuthenticated = !TextUtils.isEmpty(AuthenticationController.getInstance().getAuthToken());
if (isUserAuthenticated)
{
new Handler(Looper.getMainLooper()).post(new Runnable()
{
#Override
public void run()
{
App.from(GCMIntentService.this).fire(MessagingEvent.NEW_MESSAGE);
}
});
}
else
{
displayNotification(newUnreadCount + " New Message" + (newUnreadCount > 1 ? "s" : ""), newUnreadCount);
}
}
}
private void displayNotification(CharSequence message, int eventCount)
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean shouldDisplayMessages = preferences.getBoolean("notifications_new_message", true);
if (!shouldDisplayMessages)
{
return;
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent openMessagingScreen = new Intent(this, LandingActivity.class);
openMessagingScreen.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
openMessagingScreen.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
preferences.edit().putBoolean(MessagingFragment.PREF_MESSAGE_WAITING, true).apply();
PendingIntent notificationAction = PendingIntent.getActivity(this, 0, openMessagingScreen, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.app_name))
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setContentText(message)
.setAutoCancel(true)
.setContentIntent(notificationAction)
.setNumber(eventCount);
Notification notification = notificationBuilder.build();
notification.defaults |= Notification.DEFAULT_ALL;
try
{
notificationManager.notify(NOTIFICATION_NEW_MESSAGE, notification);
}
catch (SecurityException ex)
{
// This is required due to a bug in android
// http://stackoverflow.com/questions/13602190/java-lang-securityexception-requires-vibrate-permission-on-jelly-bean-4-2
Log.e("PPDirect", ex.getLocalizedMessage());
}
}
}
I had several questions regarding push notifications:
which call back function actually checks for arrived message and is called once push notification arrives?
Is it possible that since google have updated GCM to FCM I need to make updates to the key or migrate GCM to FCM?
Can there be some other reason?
Any help in the matter would be much appreciated.
1. which call back function actually checks for arrived message and is called once push notification arrives?
For Android, it's commonly received in onMessageReceived in the GcmListenerService. However, it may also depend whether or not the app is in background or foreground.
2. Is it possible that since google have updated GCM to FCM I need to make updates to the key or migrate GCM to FCM?
As per the note here:
Starting from Sept. 2016 new server key can only be created in the Firebase Console using the Cloud Messaging tab of the Settings panel. Existing projects that need to create a new server key can be imported in the Firebase console without affecting their existing configuration.
New users of GCM is in need of creating a Firebase projext, regardless if you are going to use FCM or not, in order to have a Server Key. For the migration steps, you can see my answer here.
3. Can there be some other reason?
This is too broad to answer. Maybe it's because of your payload structure or the implementation of your onMessageReceived(), etc.
I've searched a lot about notifications when the app is in the background or closed. I'm using Firebase Cloud Messaging by the way. It won't work for me. I've used the Android setup and when the app is in the foreground or the phone is not locked the notification is received.
When installed the token is printed correctly and subscribed to the topic.
When I send a notification when the app is active in the foreground (so screen is unlocked and app is shown) I receive the notification and title as stated in the onMessageReceived.
When I send a notification when the app is not shown but is still in recent apps and screen is unlocked I receive the notification with title and message as stated in notification payload.
When I send a notification when the app is not shown but is still in recent apps and screen is locked nothing is received.
When I send a notification when app is *closed and removed from recent apps nothing is received.
How can I change this so the app will always receive the notifications, even when closed or the phone is locked?
Ps. I read about the Doze modus with protected apps, even when I put my app with the protected ones I receive nothing. I'm testing on a Huawei P8 Lite.
AndroidManifest
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme" >
<activity android:name=".activities.MainActivity"
android:configChanges="orientation"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".services.MyAppFirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".services.FirebaseIDService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<receiver android:name=".services.NotificationReceiver" />
</application>
Gradle
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.google.firebase:firebase-core:9.2.0'
compile 'com.google.firebase:firebase-messaging:9.2.0'
}
apply plugin: 'com.google.gms.google-services'
FirebaseMessagingService
public class MyAppFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FCM Service";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO: Handle FCM messages here.
// If the application is in the foreground handle both data and notification messages here.
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated.
Log.d(TAG, "From: " + remoteMessage.getFrom());
Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
showNotification(getApplicationContext());
}
public static void showNotification(Context context){
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("FCM Message")
.setContentText("FCM Body")
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
FirebaseInstanceIDService
public class FirebaseIDService extends FirebaseInstanceIdService {
private static final String TAG = "FirebaseIDService";
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
System.out.println("Devicetoken: " + refreshedToken);
FirebaseMessaging.getInstance().subscribeToTopic("/topics/myapp");
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(refreshedToken);
}
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM InstanceID 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.
}
}
Notification payload
{
"to": "/topics/mytopic",
"priority": "high",
"notification": {
"sound": "default",
"badge": "1",
"body": "the body text",
"title": "title text"
},
"data": {
"id": "id",
"channel": "channel"
}
}
EDIT - Add code for WakeFulBroadcastReceiver
public class NotificationReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// cancel any further alarms
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmMgr.cancel(alarmIntent);
completeWakefulIntent(intent);
// start the GcmTaskService
MyAppFirebaseMessagingService.showNotification(context);
}
}
UPDATE PAYLOAD
If I change my payload to the suggested way in the comments like this it is still not working. Maybe it has something to do with the Huawei P8 Lite that I'm testing on with Android 6.0.1 installed.
{
"to": "/topics/mytopic",
"priority": "high",
"data": {
"sound": "default",
"badge": "1",
"body": "the body text",
"title": "title text"
}
}
UPDATE 2.0
I've tested on multiple devices and versions. On devices with Android 5 it was working fine, also without app open and screen locked. Only device it wasn't working was my own Huawei P8 Lite. Still can't figure out why it's not working on that one.
When the app is closed, it shutdowns the service. You must to restart the service.
On your Application class, implements ActivityLifecycleCallbacks and on onActivityDestroyed restart the service with an alarm.
public class YourApplication extends Application implements Application.ActivityLifecycleCallbacks {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityDestroyed(Activity activity) {
Intent restartService = new Intent(getApplicationContext(), MyAppFirebaseMessagingService.class);
PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(),1,restartService,PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME,5000,pendingIntent);
}
}
I also had issues with devices not receiving the notifications when they were closed, like they would be after a restart.
Turned out, it wasn't working with a DEBUG version of the solution, so that had to be tested in RELEASE MODE. For those using Android Studio, press the Green Play Button next to the debug button.
Firebase has different types of notifications, and each has special handling.
Assuming you're using a data push, you don't need special handling or a WakefulBroadcastReceiver.
If you're using a notification push, the notification will appear automatically in the system tray. You cannot do any special handling there.
Check the official documents here: https://firebase.google.com/docs/cloud-messaging/android/receive
I want to send a notification to the user when a task is assigned (i.e on a button click). When a task is assigned to the user I change some values in my server(i.e i'm performing a post method) I would like to know how to achieve this.
I have reached till registering the user when he logs in to my app & i'm storing users token id into my server I have also gone through google developers guide and have registered my app and obtained senderid and serverapikey. I will post the code till where I have reached please help me on how to proceed further.
LoginActivity
private void checkUserRegistrationToken() {
String url = URLMap.getGcmtokenUrl("gcmtoken_url");
employeeId = LoggedInUserStore.getLoggedInEmployeeId(getApplicationContext());
companyId = LoggedInUserStore.getLoggedInCompanyId(getApplicationContext());
url = url.replace("{eid}", employeeId).replace("{cid}", companyId);
final StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
tokenId = jObj.getString("TokenId");
if (tokenId.equals("null")) {
registerInBackground();
}
} catch (JSONException e) {
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("Error===" + error.toString());
}
});
request.setRetryPolicy(new VolleyRetryPolicy().getRetryPolicy());
RequestQueue queue = ((VolleyRequestQueue) getApplication()).getRequestQueue();
queue.add(request);
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcmObj == null) {
gcmObj = GoogleCloudMessaging.getInstance(LoginActivity.this);
}
tokenId = gcmObj.register(String.valueOf(R.string.gcm_defaultSenderId));
msg = "Registration ID:" + tokenId;
if (new ServiceManager(getApplicationContext()).isNetworkAvailable() && checkPlayServices()) {
String storeUrl = URLMap.getGcmtokenPostUrl();
employeeId = LoggedInUserStore.getLoggedInEmployeeId(getApplicationContext());
companyId = LoggedInUserStore.getLoggedInCompanyId(getApplicationContext());
HashMap<String, String> map = new HashMap<String, String>();
map.put("EmployeeId", employeeId);
map.put("CompanyId", companyId);
map.put("TokenId", tokenId);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, storeUrl, new JSONObject(map), new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.i(TAG, "Token has been posted in server!");
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i(TAG, "Error posting token into server!!");
}
});
request.setRetryPolicy(new VolleyRetryPolicy().getRetryPolicy());
RequestQueue queue = ((VolleyRequestQueue) getApplication()).getRequestQueue();
queue.add(request);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Token Mesage=" + msg);
return msg;
}
#Override
protected void onPostExecute(String s) {
}
}.execute();
}
As you can see that I'm storing token id of user into my server.
Now I have also created a GcmBroadcastReceiver class
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(),
NotificationService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
and I have also created a NotificationService class which has on MessageReceived method
public class NotificationService extends GcmListenerService {
public static final int notifyID = 9001;
public static final String appname = "FM Ninja";
NotificationCompat.Builder builder;
public NotificationService() {
// super("GcmIntentService");
}
#Override
public void onMessageReceived(String from, Bundle data) {
Intent resultIntent = null;
PendingIntent resultPendingIntent;
resultIntent = new Intent(this, HomeActivity.class);
resultPendingIntent = PendingIntent.getActivity(this, 0,
resultIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mNotifyBuilder;
NotificationManager mNotificationManager;
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("title")
.setTicker("New Message !")
.setContentText("first message please")
.setSmallIcon(R.mipmap.cms_launch_icon);
// Set pending intent
mNotifyBuilder.setContentIntent(resultPendingIntent);
// Set Vibrate, Sound and Light
int defaults = 0;
defaults = defaults | Notification.DEFAULT_LIGHTS;
defaults = defaults | Notification.DEFAULT_VIBRATE;
defaults = defaults | Notification.DEFAULT_SOUND;
mNotifyBuilder.setDefaults(defaults);
mNotifyBuilder.setAutoCancel(true);
// Post a notification
mNotificationManager.notify(notifyID, mNotifyBuilder.build());
}
}
The problem is even after adding permission in Manifeast when a call is made from server onMessageReceived is never getting executed Please help me.
I will also post manifeast file
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--GCM Permissions-->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<permission android:name="com.six30labs.cms.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.six30labs.cms.permission.C2D_MESSAGE" />
<application
android:name="com.six30labs.cms.general.VolleyRequestQueue"
android:allowBackup="true"
android:icon="#mipmap/cms_launch_icon"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".SplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity android:name=".LoginActivity" />
<activity android:name=".ForgotPassword" />
<activity android:name=".NoInternet" />
<activity android:name=".HomeActivity"/>
<activity android:name=".ComplaintDetailsSupervisor" />
<activity android:name=".ComplaintDetailsEmployee" />
<activity android:name=".NavBarProfile" />
<activity android:name=".ComplaintDetailsWorker" />
<activity android:name=".AssignedDetailSupervisor" />
<activity android:name=".AcceptedComplaintDetailsWorker" />
<activity android:name=".VerifyDetailSupervisor"/>
<activity android:name=".ManagerComplaintListActivity"/>
<!--<service android:name="com.six30labs.cms.storage.RegistrationIntentService"
android:exported="false"/>-->
<receiver
android:name="com.six30labs.cms.general.GcmBroadcastReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.six30labs.cms"/>
</intent-filter>
</receiver>
<!-- Register Service -->
<service android:name="com.six30labs.cms.general.NotificationService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
</application>
Server side script
public class GCMNotification
{
private CMSEntities db = new CMSEntities();
static string gcmid = "AIzaSyB7HSIF1RIvkyCnpP6KtYiy6wQ-s6YBscY";
public void AssignEmpNotification(string employeeid)
{
long id = Convert.ToInt64(employeeid);
PushBroker pushBroker = new PushBroker();
pushBroker.RegisterGcmService(new GcmPushChannelSettings(gcmid));
pushBroker.QueueNotification(new GcmNotification().ForDeviceRegistrationId("APA91bGElkVodLyubuMM90TEnfUMab0Fs6JudsjXcgIUrTrT8Zk3GezKYWc9w2gGs6pzLLq_nPSZCXU30M5iYKdRJcKZnkafWuwhnihZQ88vcwUrKhiQn6eWSqGrLCeHFblVT09IR7jy")
.WithJson(#"{""message"":""Hi Hello" + "wsfdasd" + #""",""title"":""title" + "vendorBids" + #""",""Bidsid"":""" + "1" + #""",""Eventdate"":""" + "2/2/2016" + #""",""vendorname"":""" + "name" + #"""}"));
pushBroker.StopAllServices();
}
}
The server needs to send http post request
to "https://android.googleapis.com/gcm/send" along with list of
registration ids and message as body data.
The body data is a combination of :
a. registration ids (which is a list of array)
b. message
The header is a combination of :
a. Content-Type
b. Authorization (key:project id)
List regIds = new ArrayList();
//add regIds to this list : regIds.add("value");
JSONObject data = new JSONObject ();
data.put("registration_ids", regIds);
data.put("Message", "Hello");
ApiKey is the value we received during project created in google store.
Map headers = new HashMap();
headers.put("Content-Type", "application/json");
header.put("Authorization","key=ApiKey");
After sending http post request. The google gcm server will send
the message to all users whose registration id is mentioned in http
request.
The android mobile will receive a notification from gcm server as response
which will be handled by GCMBroadcastReceiver.
Now NotificationService class will be invoked which will extent
IntentService.
In this class ,
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty() &&
GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
showNotification(intent.getStringExtra("Message"));
}
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
This method is calling "showNotification" method which has a string
parameter.
Use this message and show in notification using NotificationManager.
Hope this helps :)
Start logging the response that the server is getting after sending the notification payload to google servers. If there is a problem with the device token, you will get a detailed error like "Not Registered".
If the there is no problem in token, google server will respond you with success and message id.
Once you are assured that there is no problem with the device token, then we can think about the issues in the device code.
Other probable issue I can think of is that the SHA hash that you have added in the google developer console is not matching with the SHA hash of the keystore that you have signed the app with.
Add the SHA hash of debug.keystore as well as the production signing keystore.