This question already exists:
DevicePolicyManager, locknow when gcm message received
Closed 9 years ago.
Hoping someone can help me, as I have been stuck on this for days. I am trying to lock my device remotely when a PUSH notification arrives with a specified keyword. Else, display the message in a notification.
It correctly displays the notification but never locks the screen when the keyword is sent, nothing happens, nothing is logged.
I have confirmed the string is correctly read as I previously set it to display a custom notification if the message contained the keyword.
The app definitely has admin privileges and I can lock the screen using the same code inside an activity ... is there something else I need to do to get this to work in the gcmintentsevice ??
here is my gcmintentservice code
package library;
import com.test.LoginActivity;
import com.test.R;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* This {#code IntentService} does the actual handling of the GCM message.
* {#code GcmBroadcastReceiver} (a {#code WakefulBroadcastReceiver}) holds a
* partial wake lock for this service while the service does its work. When the
* service is finished, it calls {#code completeWakefulIntent()} to release the
* wake lock.
*/
public class GcmIntentService extends IntentService {
String messagereceived;
String Password = "LOCK";
ComponentName mDeviceAdmin;
public static final int NOTIFICATION_ID = 1;
private DevicePolicyManager mDPM;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public GcmIntentService() {
super("GcmIntentService");
}
public static final String TAG = "test";
#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.isEmpty()) { // has effect of unparcelling Bundle
/*
* Filter messages based on message type. Since it is likely that GCM will be
* extended in the future with new message types, just ignore any message types you're
* not interested in, or that you don't recognize.
*/
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server: " + extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// This loop represents the service doing some work.
for (int i = 0; i < 5; i++) {
Log.i(TAG, "Working... " + (i + 1)
+ "/5 # " + SystemClock.elapsedRealtime());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
Log.i(TAG, "Completed work # " + SystemClock.elapsedRealtime());
// Post notification of received message.
messagereceived = extras.getString("message");
if(messagereceived.equals("LOCK")){
gcmaction();
}
else{
sendNotification("Message: " + extras.getString("message"));
Log.i(TAG, "Message: " + extras.toString());
}
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
//lock method
private void gcmaction() {
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mDeviceAdmin = new ComponentName(this, AdminReceiver.class);
boolean active = mDPM.isAdminActive(mDeviceAdmin);
if (active) {
mDPM.resetPassword(Password,DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
mDPM.lockNow();
}
else Log.i(TAG,"Not an admin");
}
// Put the message into a notification and post it.
// This is just one simple example of what you might choose to do with
// a GCM message.
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LoginActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("ParentalKontrol")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
Your my_admin.xml contains below?
<device-admin xmlns:android="http://schemas.android.com/apk/res/android" >
<uses-policies>
<force-lock />
</uses-policies>
</device-admin>
Related
I didn't change anything in my app since several months but starting from 11th June I'm not able to receive any message sent by Firebase cloud messaging. It has been reported by several customers and I can confirm that it doesn't work anymore. No firmware change or anything at least on my phone. I used exactly the same code you find here. The message is sent, the callback onMessageSent in the FirebaseMessagingService is called correctly, but I'm not able to receive anything using the same account on another phone. Is there any big change I miss it? My configuration doesn't use a server so I can't check any log server side. Any tips?
The official reply I've gotten from Firebase is: they simply don't support the no-server configuration anymore.
So after 30 emails with Firebase support here's the surprise: there's nothing wrong with my code/project, and the funny thing is that they don't know why and how it worked before! Unfortunately, they didn't advise anyone about this big change, so any app based on Android device groups without a server doesn't work anymore.
Here is a possible answer to your question -
Firstly, you say that 'But I'm not able to receive anything using the same account on another phone'. Device Messaging via FCM uses the device registration token as the point of contact and not the account. Therefore, you might not receive messages from FCM with the same account on a different device.
Secondly, you can only have 20 devices registered to each token group. The Firebase Docs also mention that you usually need an app server to do device to device messaging.
Remember that this kind of device to device messaging is used only for notifications, not for sending chat messages or anything of that kind. If you want to implement notifications without an app server, try this -
import android.app.IntentService;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.legacy.content.WakefulBroadcastReceiver;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
public class NotificationIntentService extends IntentService {
private static int NOTIFICATION_ID = 1;
private static final String ACTION_START = "ACTION_START";
private static final String ACTION_DELETE = "ACTION_DELETE";
public NotificationIntentService() {
super(NotificationIntentService.class.getSimpleName());
}
public static Intent createIntentStartNotificationService(Context context) {
Intent intent = new Intent(context, NotificationIntentService.class);
intent.setAction(ACTION_START);
return intent;
}
public static Intent createIntentDeleteNotification(Context context) {
Intent intent = new Intent(context, NotificationIntentService.class);
intent.setAction(ACTION_DELETE);
return intent;
}
#Override
protected void onHandleIntent(Intent intent) {
Log.w(String.valueOf(new NotificationIntentService()), "onHandleIntent, started handling a notification event");
try {
String action = intent.getAction();
if (ACTION_START.equals(action)) {
processStartNotification();
}
if (ACTION_DELETE.equals(action)) {
processDeleteNotification(intent);
}
} finally {
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
}
private void processDeleteNotification(Intent intent) {
// Log something?
}
private void processStartNotification() {
// Do something. For example, fetch fresh data from backend to create a rich notification?
NOTIFICATION_ID += 1;
Intent intent = new Intent(NotificationIntentService.this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(NotificationIntentService.this, 0 /* Request code */, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(NotificationIntentService.this, channelId)
.setSmallIcon(R.drawable.chat)
.setContentTitle("Title")
.setContentText("Text")
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
notificationBuilder.setContentIntent(pendingIntent);
notificationBuilder.setDeleteIntent(NotificationEventReceiver.getDeleteIntent(NotificationIntentService.this));
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"channel_id",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
}
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.legacy.content.WakefulBroadcastReceiver;
import java.util.Calendar;
import java.util.Date;
public class NotificationEventReceiver extends WakefulBroadcastReceiver {
private static final String ACTION_START_NOTIFICATION_SERVICE = "ACTION_START_NOTIFICATION_SERVICE";
private static final String ACTION_DELETE_NOTIFICATION = "ACTION_DELETE_NOTIFICATION";
private static final int NOTIFICATIONS_INTERVAL_IN_FIFTEEN_MINUTES = 0;
public static void setupAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent alarmIntent = getStartPendingIntent(context);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
getTriggerAt(new Date()),
600000,
alarmIntent);
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Intent serviceIntent = null;
if (ACTION_START_NOTIFICATION_SERVICE.equals(action)) {
Log.w(getClass().getSimpleName(), "onReceive from alarm, starting notification service");
serviceIntent = NotificationIntentService.createIntentStartNotificationService(context);
} else if (ACTION_DELETE_NOTIFICATION.equals(action)) {
Log.w(getClass().getSimpleName(), "onReceive delete notification action, starting notification service to handle delete");
serviceIntent = NotificationIntentService.createIntentDeleteNotification(context);
}
if (serviceIntent != null) {
startWakefulService(context, serviceIntent);
}
}
private static long getTriggerAt(Date now) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(now);
//calendar.add(Calendar.HOUR, NOTIFICATIONS_INTERVAL_IN_HOURS);
return calendar.getTimeInMillis();
}
private static PendingIntent getStartPendingIntent(Context context) {
Intent intent = new Intent(context, NotificationEventReceiver.class);
intent.setAction(ACTION_START_NOTIFICATION_SERVICE);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getDeleteIntent(Context context) {
Intent intent = new Intent(context, NotificationEventReceiver.class);
intent.setAction(ACTION_DELETE_NOTIFICATION);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public final class NotificationServiceStarterReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationEventReceiver.setupAlarm(context);
}
}
I'm sorry, but I won't be able to tell you much more without seeing the code.
Hope this helps.
Sources -
Firebase Docs - https://firebase.google.com/docs/cloud-messaging/concept-options#senderid
Firebase Docs - https://firebase.google.com/docs/cloud-messaging/android/device-group#managing-device-groups-on-android-client-apps
Migrate from GoogleAuthUtil and Plus.API
If you integrated with Google Sign-In in the past using GoogleAuthUtil.getToken or Plus.API, you should migrate to the newest Sign-In API for greater security and a better user experience.
Android client side
Remove the GET_ACCOUNTS (Contacts) permission if you request it
Switch any code using GoogleAuthUtil, Plus.API, AccountPicker.newChooseAccountIntent(), or AccountManager.newChooseAccountIntent() to Auth.GOOGLE_SIGN_IN_API with GoogleSignInOptions.Builder.requestIdToken(...) configuration.
https://developers.google.com/identity/sign-in/android/migration-guide
Please note that the Migrate from legacy HTTP to HTTP v1 Cautions: Any apps that use device group messaging may prefer to not upgrade. HTTP v1 does not support this feature of the FCM legacy API.
I have GCM with php AND my code work without any problem
its idea get notification from php and show it on notification bar
and when click on notification its show me the message on TextView
i just want to update that TextView on Activity without click on notification
now let me show the code for understand me
this the home ACtivity thats contain the TextView
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class Home_Activity extends Activity {
TextView view_msg;
// i try make it public static TextView view_msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
view_msg = (TextView) findViewById(R.id.message);
}
public void onResume()
{
super.onResume();
String str = getIntent().getStringExtra("msg");
if (str != null) {
view_msg.setText(str);
}
}
}
we can see its
onResume
check if there are any extra data but i want to update that TextView just when new gcm message recive
now the code that recive message
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import com.medo.alex.Home_Activity;
import com.medo.alex.R;
import com.google.android.gms.gcm.GoogleCloudMessaging;
public class Gcm_Notification_Intent_Service extends IntentService {
// Sets an ID for the notification, so it can be updated
public static final int notifyID = 9001;
public Gcm_Notification_Intent_Service() {
super("GcmIntentService");
}
#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_SEND_ERROR
.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
.equals(messageType)) {
sendNotification("Deleted messages on server: "
+ extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
.equals(messageType)) {
sendNotification("" + extras.get(Gcm_Application_Constants.MSG_KEY)); //When Message is received normally from GCM Cloud Server
}
}
Gcm_Broadcast_Receiver.completeWakefulIntent(intent);
}
private void sendNotification(String msg) {
Intent resultIntent = new Intent(this, Home_Activity.class);
resultIntent.putExtra("msg", msg);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
resultIntent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mNotifyBuilder;
NotificationManager mNotificationManager;
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("NEW MESSAGE")
.setSmallIcon(R.mipmap.ic_launcher);
// 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);
// Set the content for Notification
mNotifyBuilder.setContentText("YOU HAVE NEW MESSAGE");
// Set autocancel
mNotifyBuilder.setAutoCancel(true);
// Post a notification
mNotificationManager.notify(notifyID, mNotifyBuilder.build());
// MainActivity.view_msg.setText(msg); // iam try use this and make my view_msg public and static when i add this line and run my project and say progect name has stopped just when add this line
}
}
now my question its how to update the view_msg and its on Home_activity
when recive messgae from Gcm i write in code what i try to do but its say my project was stopped now forget my trying and just give me idea about how to update the view_msg TextView from another class
It seems like you need Service bounding
some awesome example you can find HERE
just try to bind your Activity and when it can be done just send Message with content. when Activity is missing e.g. keep Notification popping
I wrote an IntentService for GCM Push Notifications.
I receive the Messages but something's wrong with displaying my Notification to the user.
Here's my Code:
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
public GcmIntentService() {
super("GcmIntentService");
}
public static final String TAG = "GCM test";
#Override
protected void onHandleIntent(Intent intent) {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!intent.getExtras().isEmpty()) { // has effect of unparcelling Bundle
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + intent.getExtras().toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server: " + intent.getExtras().toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Post notification of received message.
sendNotification("message:\n" + intent.getStringExtra("message"));
Log.i(TAG, "Received: " + intent.getExtras().toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void sendNotification(String msg) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notif)
.setContentTitle("My notification")
.setContentText(msg);
Intent resultIntent = new Intent(this, PopupMessageActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(PopupMessageActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
I don't see the mistake. I mean, I copied the code from the Androids developer guide.
The only thing that this code does, is that the small icon (in this case "ic_notif") is showing in the notification-bar of the phone.
But there's no Text or Notification that pops up to the user.
I use android-studio.
My debug device is an huawai u8666e with android 4.0.3 (API 15).
At least i want this API level to be my minimum requirement for this app.
What you are seeing is normal designed Android behaviour for versions before Lollipop.
The design logic is that this method creates a cleaner interface and will not interrupt the user's current actions by placing a popup in front of their face. (there is a lot of debate over which method is better - iOS popups vs Android notifications).
Lollipop changes this slightly by creating a small popup at the top of the device window when a Notification is created.
If you really want to force a popup dialog to be shown, you should be looking at designing a "full screen" Notification.
See the Android Developer docs:
Notication.Builder.setFullScreenIntent(Intent)
Using this method, you can create a new Activity with any custom layout you want, and launch that instead of placing the Notification in the status bar.
(full implementation of a full screen notification would be beyond the scope of this post)
I would recommend against forcing full screen notifications except in rare cases, such as an Alarm Clock, or Phone Call app. I would, instead, recommend that you stick to the way Android was designed and work with the OS.
I'm using GCM to get notified when an image is posted, and then I download and process it:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
DataUtils.log("In GcmBroadcastReceiver! threadname is " + Thread.currentThread().getName());
// 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);
}
}
This is the beginning of my GcmIntentService:
public class GcmIntentService extends IntentService
{
public static final int NOTIFICATION_ID = 1;
public GcmIntentService()
{
super("GcmIntentService");
}
#Override
protected void onHandleIntent(Intent intent)
{
DataUtils.log("In GcmIntentService onHandleIntent(), threadname is " + Thread.currentThread().getName());
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.isEmpty()) // has effect of unparcelling Bundle
{
/*
* Filter messages based on message type. Since it is likely that GCM will be
* extended in the future with new message types, just ignore any message types you're
* not interested in, or that you don't recognize.
*/
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType))
{
DataUtils.log("In GcmIntentService - Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType))
{
DataUtils.log("In GcmIntentService - Deleted messages on server: " + extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
String notificationType = extras.getString(MyAppApi.GCM_MSG_TYPE_KEY);
if(DataUtils.isEmpty(notificationType)) {
DataUtils.log("In GcmIntentService - notificationType is empty!");
} else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_NEW_WALLPAPER)) {
//We're about to receive a new image!
DataUtils.log("In GcmIntentService - Receiving a new image!");
processNewWallpaper();
} else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_FRIEND_NOTIFICATION)) {
//We're about to receive a friend notification
DataUtils.log("In GcmIntentService - Receiving a friend notification!");
processFriendNotification();
} else {
//Unknown
DataUtils.log("In GcmIntentService - Receiving unknown message type! " + notificationType);
}
} else {
DataUtils.log("In GcmIntentService - Unknown GCM message: " + extras.toString());
}
}
//Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
}
It seems that randomly the service will die. From the log:
01-13 20:00:44.436: I/ActivityManager(375): Process com.grakk.android (pid 23227) has died.
01-13 20:00:44.444: W/ActivityManager(375): Scheduling restart of crashed service com.grakk.android/.GcmIntentService in 11426ms
What the code does when it receives a GCM message is to download an image, then it shows the user a notification (this is similar to a normal chat app).
A tester told me that once he received an image but didn't get the notification, which means that the service itself is started and does part of the work, but doesn't complete it.
The notification code is run in processNewWallpaper(), along with the download and processing of the image. Here's the code:
...
if(senderContact == null) {
sendNotification(null, message, true);
} else {
sendNotification(senderContact.getName(), message.trim(), false);
}
...
Notification method:
...
// Put the message into a notification and post it. This is just one simple example
// of what you might choose to do with a GCM message.
#SuppressWarnings("deprecation")
#TargetApi(16)
private void sendNotification(String name, String message, boolean isAnonymous)
{
Context context = GcmIntentService.this;
NotificationManager mNotificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ContactsActivity.class), 0);
Notification.Builder mBuilder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(context.getString(R.string.app_name));
String textToShow = null;
if(DataUtils.isEmpty(message))
{
if(isAnonymous) {
textToShow = context.getString(R.string.notification_text_anonymous);
} else {
textToShow = String.format(getResources().getString(R.string.notification_text_friend), name);
}
} else {
textToShow = message;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mBuilder.setStyle(new Notification.BigTextStyle().bigText(textToShow));
}
mBuilder.setContentText(textToShow);
mBuilder.setAutoCancel(true);
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
mBuilder.setSound(alarmSound);
mBuilder.setContentIntent(contentIntent);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
} else {
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.getNotification());
}
}
I am able to reproduce this by sending myself an image, and then pressing the Android back button repeatedly until I am no longer in the app. I can follow the log messages that show that the image is downloaded, however it dies before the notification is shown.
This doesn't always happen. Sometimes the notification is shown, sometimes it's not.
I'm not sure what are probable causes, nor how to debug this. Any tips?
Have you called the OnCreate() in the GcmIntentService class?
Some sample code below:
public class GcmIntentService extends IntentService {
String mes;
private Handler mHandler;
public GcmIntentService() {
super("GcmIntentService");
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
mes = extras.getString("title");
showToast();
Log.i("GCM", "Recevied: (" + messageType + ") " + extras.getString("title"));
GcmReceiver.completeWakefulIntent(intent);
}
public void showToast() {
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), mes, Toast.LENGTH_LONG).show();
}
});
}
}
EDIT: Add useful youtube tutorial for GCM here.
Sorry that I'm using answer (I can't comment yet).
I would try extracting the call to sendNotification from processNewWallpaper to right after processNewWallpaper(). If that didn't work you should post your code in processNewWallpaper(). My guess is that in some cases your code crashes inside processNewWallpaper and skips the sendNotification but since its being handled it wouldn't throw anything.
Also I have noticed that apps act differently if they'v been open in background or completely closed (use running apps key and close your app there). If you can consistently reproduce the problem it will be easier to solve it.
Is that all the logcat you have? Any exceptions or stack traces from the "crashed" service?
However, an idea, are you downloading images asynchronously and in a callback creating the notification?
You are releasing the wake lock at the end of onHandleIntent which will be called before any async code is executed. Releasing the wake lock will kill the service if the screen is off.
What you would need to do is conditionally release the wake lock in onHandleIntent only if no async work needs to be done. And in the callback for any async work release the wake lock. Just make sure there's no execution path that doesn't release the wake lock!
Hope that's it!
I trying to make push notifications work for my android app. The server seems OK since I receive notifications on my android 4 device. But I have other devices with android 2.2.1 and 2.3.4 that don't receive the notifications.
Here's my C2DMReceiver :
package vex.android;
import java.io.IOException;
import vex.android.settings.Local;
import vex.android.tool.Resources;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.google.android.c2dm.C2DMBaseReceiver;
public class C2DMReceiver extends C2DMBaseReceiver {
public C2DMReceiver() {
super(Local.PushNotificationEmail);
}
#Override
public void onError(Context context, String errorId) {
Log.e("VEX-PUSHNOTIFICATION", "Error " + errorId);
}
#Override
protected void onMessage(Context context, Intent intent) {
String saleTitle = Resources.getString("pushnotificationtitle", context);
String saleMessage = intent.getStringExtra("salemessage");
String SaleId = intent.getStringExtra("saleid");
String isMultiSale = intent.getStringExtra("ismultisale");
Boolean multisale = (isMultiSale != null && isMultiSale.length()>0) ? Boolean.parseBoolean(isMultiSale.trim()) : false;
Integer saleid = (SaleId != null && SaleId.length()>0) ? Integer.parseInt(SaleId.trim()) : -1;
if(saleMessage == null || saleMessage.length() <= 0 ) saleMessage = Resources.getString("pushnoticationmessage", context);
createNotification(context, saleTitle, saleMessage, saleid, multisale);
}
public void createNotification(Context context,String SaleTitle, String SaleMessage, Integer saleid, Boolean multisale) {
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.applicationicon,
"Message received", System.currentTimeMillis());
// Hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(context, MainApplication.class);
intent.putExtra("saleid", saleid);
intent.putExtra("ismultisale", multisale);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); // without flag a changed saleid wont be passed
notification.setLatestEventInfo(context, SaleTitle, SaleMessage, pendingIntent);
notificationManager.notify(saleid, notification);
}
#Override
public void onRegistered(Context context, String registrationId)
throws IOException
{
Local.setRegistrationId(registrationId);
}
#Override
public void onUnregistered(Context context)
{
Log.i("VEX-DEBUG", "successfully unregistered with C2DM server");
}
}
I think the problem is there because event if I send the notification manually (with curl) it doesn't work with android 2.2 and 2.3. Any idea? Thanks
I've not had any problems using C2DM on devices on older Android versions.
I suggest you get more devices to test with, and check your code - the problem is not an issue of a lack of C2DM support for older devices - it works on Android from v2.2 onwards.
C2DM uses Google Messaging Service. GTalk uses this service as well. Sometimes this service might be turned off. To check all related information just type in this code - *#*#8255#*#*
C2DM is available on devices with android >= 2.2