I want to send push notification to my android device and place them such that it is hard for the user to miss the push notification. Especially if the app is not running or is in background. I already know how to handle gcm push notification while the app is running. However, I am interested in how to customly display gcm message if the app is not running.
This is the situation I have right now:
App is closed/not running
I send a push notification with my python script and it is recieved on my device
A sound is played the screen keeps dark, no vibration
When hitting the sleep/wake button I can see a small notification
This is what I want:
App is closed/not running, device is locked, screen is off
I send a push notification via gcm to my device
when the message is recieved by my the device the following should happen
Phone should vibrate
Phone screen should turn on and show a custom notification
Phone should play a sound (okay, that already works)
After an extensive search on the net I have not found any resource yet that explains how to do that.
Can anybody provide a some guidance to me?
You can achieve that using WakefulBroadcastReceiver which prevents phone from sleeping and check GCM notification even if your app is not open.
Below are steps to achieve so
STEP-1 : You've to add WAKE_LOCK, INTERNET and other permissions in manifest
<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="your.package.name.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="your.package.name.permission.C2D_MESSAGE"/>
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
STEP-2 : Register WakefulBroadcastReceiver and GCMMessageHandler also in manifest file under <application> tag
<receiver android:name=".gcm.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="com.hmkcode.android.gcm"/>
</intent-filter>
</receiver>
<service android:name=".gcm.GcmMessageHandler"/>
STEP-3 : Definition for GcmBroadcastReceiver is below
package your.package.name.gcm;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(),
GcmMessageHandler.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
STEP-4 : Definition of GcmMessageHandler is
package your.package.name.gcm;
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 android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import your.package.name.R;
import your.package.name.SplashScreen;
public class GcmMessageHandler extends IntentService {
public GcmMessageHandler() {
super("GcmMessageHandler");
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
protected void onHandleIntent(Intent intent) {
final 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)) {
Log.e("GCM", "Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
Log.e("GCM", "Deleted messages on server: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
sendNotification(extras);
}
}
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void sendNotification(Bundle extras) {
try {
String message = extras.getString("message");
if(message != null) {
Log.e(tag, "Notification received " + message);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setSmallIcon(R.mipmap.ic_launcher);
mBuilder.setContentTitle("TITLE");
mBuilder.setContentText(message);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 1, new Intent(this, SplashScreen.class), PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setDefaults(Notification.DEFAULT_ALL); // this line sets the default vibration and sound for notification
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
} else {
Log.d(tag, "Message is empty.");
}
} catch (Exception ex) {
Log.e(tag, Log.getStackTraceString(ex));
}
}
}
Hope it'll help you.
Related
I am not receiving push notification for my app. I have included the manifest files, the MainActivity file, the MyFireBaseMessagingService file and the build.gradle file and the googl-services.json. I really am stumped on what the issue could be. I go to Firebase composer to write message but nothing is received. I can hard code values in strings file and I receive a notification but I wanted something more dynamic. That is why I am trying to figure out how to send push notifications from FCM but I seem to be missing something or inputting something incorrectly.
MainAcitivity file
package com.example.testaaedapp;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import java.net.URI;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageButton logoButton;
private ImageView blueButton, greenButton, orangeButton, yellowButton, redButton, darkBlueButton;
public Button callButton;
private final String CHANNEL_ID = "Alerts";
private final int notificationId = 001;
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
logoButton = findViewById(R.id.imageButton);
logoButton.setOnClickListener(this);
blueButton = findViewById(R.id.bluepuzzlepiece);
blueButton.setOnClickListener(this);
redButton = findViewById(R.id.redpuzzlepiece);
redButton.setOnClickListener(this);
greenButton = findViewById(R.id.greenpuzzlepiece);
greenButton.setOnClickListener(this);
orangeButton = findViewById(R.id.orangepuzzlepiece);
orangeButton.setOnClickListener(this);
yellowButton = findViewById(R.id.yellowpuzzlepiece);
yellowButton.setOnClickListener(this);
darkBlueButton = findViewById(R.id.darkbluepuzzlepiece);
darkBlueButton.setOnClickListener(this);
callButton = findViewById(R.id.button);
callButton.setOnClickListener(this);
displayNotification();
// Get token
// [START retrieve_current_token]
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
// Log and toast
String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
// [END retrieve_current_token]
}
int requestCode = 0;
public void onClick(View view) {
if (view.getId() == R.id.button && ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
Log.d("STATE", "Call Button DOES NOT WORK");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, requestCode);
return;
} else if (view.getId() == R.id.button && ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
// else if (view.getId() == R.id.button && ActivityCompat.checkSelfPermission(MainActivity.this,
// Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
Log.d("STATE", "Call Button DOES WORK");
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:480-240-9255"));
startActivity(callIntent);
} else {
switch (view.getId()) {
case R.id.imageButton:
setContentView(R.layout.activity_main);
break;
case R.id.bluepuzzlepiece:
break;
case R.id.redpuzzlepiece:
Intent blogIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.aaed.org/blog"));
startActivity(blogIntent);
break;
case R.id.greenpuzzlepiece:
Intent videoIntent = new Intent(Intent.ACTION_VIEW);
videoIntent.setData(Uri.parse("https://www.youtube.com/channel/UCUwPShLvnCOTeQILvAtneOw"));
startActivity(videoIntent);
break;
case R.id.yellowpuzzlepiece:
Intent secondActivity = new Intent(this, SecondActivity.class);
startActivity(secondActivity);
break;
case R.id.orangepuzzlepiece:
Intent webIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.aaed.org"));
startActivity(webIntent);
break;
case R.id.darkbluepuzzlepiece:
Intent schoolIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.aaed.org/online-course-content"));
startActivity(schoolIntent);
}
}
}
public void displayNotification () {
createNotificationChannel();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_puzzlepieces)
.setContentTitle("Test Message")
.setContentText("This is text")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(getString((R.string.another_string))));
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
// notificationId is a unique int for each notification that you must define
notificationManager.notify(notificationId, builder.build());
}
public void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
/
/ Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == requestCode)
{
if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:480-240-9255"));
startActivity(callIntent);
}
}
MyFirebaseMessagingService file
package com.example.testaaedapp;
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.core.app.NotificationCompat;
import com.example.testaaedapp.MainActivity;
import com.example.testaaedapp.R;
import com.google.firebase.messaging.FirebaseMessagingService;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
#Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
// 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(token);
}
// [END on_new_token]
Manifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testaaedapp">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<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=".MapsActivity"
android:label="#string/title_activity_maps"></activity>
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="AAED" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/ic_puzzlepieces" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/colorAccent" />
<activity
android:name=".MainActivity"
android:label="Autism Academy"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="Locations"></activity>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<!-- [START firebase_service] -->
<service
android:name=".MyFirebaseMessagingService"
android:exported="false"
android:enabled="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- [END firebase_service] -->
</application>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
</manifest>
I've had a similar issue. I've tried everything - in my opinion the main reason is that all the services are, sooner or later, being killed by the system. The only way is to make sure to deliver the notification to the system tray not to the application. To do that you need to use Data message notifications.
There are 2 types of FCM notifications: Notification message and Data message.
Data messages are delivered to system tray and are always display - even if service is not running.
Notification message looks like:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
}
}
}
and triggers method OnMessageReceaved() of FirebaseMessagingService. Many devices (especially Huawei and Xiaomi) try to do everything to kill background services to prevent battery drain. So the FirebaseMessagingService isn't the best way to handle notifications.
Second type is Data message:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
This type is handled by the system tray, so you don't need any of service running to get the notification. Its much more convenient method, but as far i know, it can't be achieved with the console.
You would probably need server API to send Data message.
Read this for more details.
I'm trying firebase push notification. I did everything it said in the tutorial, but it isn't working.
FirebaseMessagingService:
package com.example.firebasenf.firebasenf;
import com.google.firebase.messaging.FirebaseMessagingService;/
public class MyFirebaseMessagingService extends FirebaseMessagingService {
}
FirebaseInstanceIdService:
package com.example.firebasenf.firebasenf;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
}
Manifest:
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
you need to add some methods to your code :
FirebaseMessagingService:
package com.example.firebasenf.firebasenf;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage){
Intent intent = new Intent(this,MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setContentTitle("Application Title");
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0,notificationBuilder.build());
}
}
FirebaseInstanceIdService :
package com.example.firebasenf.firebasenf;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String REG_TOKEN = "REG_TOKEN";
#Override
public void onTokenRefresh(){
String recent_token = FirebaseInstanceId.getInstance().getToken();
Log.d(REG_TOKEN,recent_token);
}
}
you need to override onMessageReceived in MyFirebaseMessagingService class to do some actions when notification is pushed.
doc
By overriding the method FirebaseMessagingService.onMessageReceived,
you can perform actions based on the received RemoteMessage object and
get the message data
#Override
public void onMessageReceived(RemoteMessage remoteMessage)
{
Log.d(TAG, "From: " + "Notification Received");
// TODO(developer): Handle FCM messages here.
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0)
{
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
//You may want show notification
}
keep in mind onMessageReceived will only triggered if the app in foreground
When your app is in the background, Android directs notification
messages to the system tray. A user tap on the notification opens the
app launcher by default.
I've used the quickblox chat sample and it worked fine, But I wanted to use push notifications in it.. So I followed some tutorials and read the sample and just done like it.. But when I push notification from QuickBlox Admin Panel: Messages no thing happens in my application.. No Logs, No Notifications.. No Thing..!
I'm sure that the project number and api key are correct..
Here's the manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.quickblox.sample.chat"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="19" />
<permission
android:name="com.quickblox.sample.chat.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.quickblox.sample.chat.permission.C2D_MESSAGE" />
<!-- 5. Add the following 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" />
<!-- Access to device info -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".ApplicationSingleton"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".ui.activities.SplashActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.activities.NewDialogActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait" />
<activity
android:name="com.quickblox.sample.chat.DialogsActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait" />
<activity
android:name=".ui.activities.ChatActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<receiver
android:name=".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.quickblox.sample.chat" />
</intent-filter>
</receiver>
<!-- 2. Add the following intent service: -->
<service android:name=".GCMIntentService" />
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
GcmBroadcastReceiver :
package com.quickblox.sample.chat;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(), GCMIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
Log.d("Login", "Receiver - Received Message!");
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
GCMIntentService :
package com.quickblox.sample.chat;
import android.app.IntentService;
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 android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.quickblox.sample.chat.definitions.Consts;
public class GCMIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private static final String TAG = GCMIntentService.class.getSimpleName();
private NotificationManager notificationManager;
public GCMIntentService() {
super(Consts.GCM_INTENT_SERVICE);
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "new push");
Bundle extras = intent.getExtras();
GoogleCloudMessaging googleCloudMessaging = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = googleCloudMessaging.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)) {
processNotification(Consts.GCM_SEND_ERROR, extras);
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_DELETED.equals(messageType)) {
processNotification(Consts.GCM_DELETED_MESSAGE, extras);
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Post notification of received message.
processNotification(Consts.GCM_RECEIVED, extras);
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
// 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 processNotification(String type, Bundle extras) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
final String messageValue = extras.getString("message");
Intent intent = new Intent(this, DialogsActivity.class);
intent.putExtra(Consts.EXTRA_MESSAGE, messageValue);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
intent, 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(Consts.GCM_NOTIFICATION)
.setStyle(
new NotificationCompat.BigTextStyle()
.bigText(messageValue))
.setContentText(messageValue);
mBuilder.setContentIntent(contentIntent);
notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
I have the following code for registration in activity :
gcm = GoogleCloudMessaging.getInstance(this);
regId = getRegisterationId(this);
Log.d("Login", "ID: " + regId);
if (regId.isEmpty()) {
new Thread(new Runnable() {
#Override
public void run() {
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(DialogsActivity.this);
}
regId = gcm.register(Consts.PROJECT_NUMBER);
Log.d("Login", "Registered! ID: " + regId);
runOnUiThread(new Runnable() {
#Override
public void run() {
subscribeToPushNotifications(regId);
}
});
} catch (Exception e) {
Log.d("Login", "Reg e: " + e);
}
}
}).start();
} else {
Log.d("Login", "Already Exist");
subscribeToPushNotifications(regId);
} gcm = GoogleCloudMessaging.getInstance(this);
regId = getRegisterationId(this);
Log.d("Login", "ID: " + regId);
if (regId.isEmpty()) {
new Thread(new Runnable() {
#Override
public void run() {
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(DialogsActivity.this);
}
regId = gcm.register(Consts.PROJECT_NUMBER);
Log.d("Login", "Registered! ID: " + regId);
runOnUiThread(new Runnable() {
#Override
public void run() {
subscribeToPushNotifications(regId);
}
});
} catch (Exception e) {
Log.d("Login", "Reg e: " + e);
}
}
}).start();
} else {
Log.d("Login", "Already Exist");
subscribeToPushNotifications(regId);
}
public void subscribeToPushNotifications(String regId) {
String deviceId = ((TelephonyManager) getBaseContext()
.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
QBMessages.subscribeToPushNotificationsTask(regId, deviceId,
QBEnvironment.DEVELOPMENT,
new QBEntityCallbackImpl<ArrayList<QBSubscription>>() {
#Override
public void onSuccess(ArrayList<QBSubscription> result,
Bundle params) {
Log.d("Login", "Successfully Registered");
}
#Override
public void onError(List<String> errors) {
Log.d("Login", "e : " + errors);
}
});
}
private String getRegisterationId(Context context) {
SharedPreferences prefs = getGCMPreferences(context);
String registerationId = prefs.getString(PROPERTY_REG_ID, "");
if (registerationId.isEmpty()) {
Log.d("Login", "Not registered!");
return "";
}
return registerationId;
}
private SharedPreferences getGCMPreferences(Context context) {
return getSharedPreferences(DialogsActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
private void storeRegisterationId(Context context, String regId) {
final SharedPreferences prefs = getGCMPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.commit();
}
And I get that registration done successfully.. :S
I've been searching for the solution for 2 days now :/ Any help will be appreciated.. Thanks.
I don't have enough reputation to comment, so I am putting this as a possible answer -
I have been working through the same for the last couple of days, and here are a couple of things to check:
When you set up the GCM API key, did you set any IPs? You should leave this blank to accept all.
When you say the registration is done successfully, do you mean you get the onSuccess Log message from subscribeToPushNotificationsTask? Check your device id; I was getting null for deviceId from:
String deviceId = ((TelephonyManager)getBaseContext().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
So instead I had to use
String deviceId = Secure.getString(this.getContentResolver(), Secure.ANDROID_ID);
What options were you selecting in the Quickblox Admin Panel > Messages? Be sure you are selecting "GCM (Android Push)" under the channel option.
Lastly, make sure you are using all production or all development for your environments (QBEnvironment.DEVELOPMENT). I somehow ended up with a mix, and switched them all to production now.
My push notifications are now working, so perhaps I can help out more if you have specific questions.
I know the AlarmManagerBroadcastReceiver will stay system to keep watch over SMS after I installed the .apk
Will the AlarmManagerBroadcastReceiver expend battery even if I never receive s SMS?
Do I need disable the AlarmManagerBroadcastReceiver when I stop watch SMS ?
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.enabledisablebroadcastreceiver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.code4reference.enabledisablebroadcastreceiver.EnableDisableBroadcastReceiver"
android:label="#string/title_activity_enable_disable_boradcast_receiver" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Broadcast receiver -->
<receiver android:name="com.code4reference.enabledisablebroadcastreceiver.AlarmManagerBroadcastReceiver" >
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<service android:name="com.code4reference.enabledisablebroadcastreceiver.MyInternetServer"></service>
</application>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
</manifest>
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;
import android.util.Log;
public class AlarmManagerBroadcastReceiver extends BroadcastReceiver {
private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction() != null && ACTION.compareToIgnoreCase(intent.getAction()) == 0) {
Object[] pduArray = (Object[]) intent.getExtras().get("pdus");
SmsMessage[] messages = new SmsMessage[pduArray.length];
for (int i = 0; i < pduArray.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pduArray[i]);
//Log.d("CWCGR1",
// "From: " + messages[i].getOriginatingAddress()+
// " Msg: " + messages[i].getMessageBody());
HandleMsg(context,messages[i].getOriginatingAddress(), messages[i].getMessageBody());
}
}
}
private void HandleMsg(Context context,String address, String body ){
Intent msgIntent = new Intent(context,MyInternetServer.class);
msgIntent.putExtra("address", address);
msgIntent.putExtra("body", body);
context.startService(msgIntent);
}
}
import com.example.enabledisablebroadcastreceiver.R;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class EnableDisableBroadcastReceiver extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnExit=(Button)findViewById(R.id.btnExit);
btnExit.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
finish();
}
});
}
public void enableBroadcastReceiver(View view){
ComponentName receiver = new ComponentName(this, AlarmManagerBroadcastReceiver.class);
PackageManager pm = this.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Toast.makeText(this, "Enabled broadcast receiver", Toast.LENGTH_SHORT).show();
}
public void disableBroadcastReceiver(View view){
ComponentName receiver = new ComponentName(this, AlarmManagerBroadcastReceiver.class);
PackageManager pm = this.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Toast.makeText(this, "Disabled broadcst receiver", Toast.LENGTH_SHORT).show();
}
}
As discussed here, an incoming SMS belongs to a special group of low-level system events that will cause the CPU to wake from the battery-saving "sleep mode" (this group includes incoming calls, incoming mobile data packets and AlarmManager events). The device's GSM radio will listen to incoming SMS regardless of your broadcast receiver being set or not. When an SMS arrives, the system will choose which receiver(s) will handle it. Thus, having a SMS broadcast receiver registered has no impact in the battery consumption. Besides, energy consumption due to a registered receiver in memory is not significant.
You can unregister the broadcast receiver programmatically if there is a functional requirement to do so.
Because you have fined your AlarmManagerBroadcastReceiver in the android manifest, android will wake up your app and call the AlarmManagerBroadcastReceiver every time an sms is received.
If no SMS are received, your app won't be woken up so there won't be any impact on battery.
If you want control over when to receive those broadcasts, register and unregister your receiver programmatically through the registerReceiver(BroadcastReceiver receiver, IntentFilter filter) method on a Context (e.g. Activity).
I'm trying to create a reminder application that pops up a reminder in the status bar. As I understand, the Android NotificationManager is the standard tool to use to schedule notifications however a BootService is needed in order to re-schedule notifications as scheduled events do not survive across boots.
I've pieced together an application below which schedules a single reminder at boot time, and pops up a notification in the status bar. If you click the notification, it launches MainActivity which, in the future, will have options to add more reminders, delete them, or whatever.
The problem I'm having is that the reminder works correctly the first time however it seems to be re-scheduling itself and popping up again at random times for some reason. Should I be launching another activity rather than the one which installs the BootAlarmService?
UPDATE:
So I think I found some clues with logcat. Apparently the Service is crashing and being restarted which is resetting the Notification. Any ideas why this is?
UPDATE II : code changed to working model
ActivityManager I No longer want com.example.alarm_boot_test (pid 1428): hidden #16
ActivityManager W Scheduling restart of crashed service com.example.alarm_boot_test/.BootAlarmService in 5000ms
ActivityManager W Scheduling restart of crashed service com.example.alarm_boot_test/.NotificationAlarmService in 15000ms
ActivityManager I Start proc com.example.alarm_boot_test for service com.example.alarm_boot_test/.BootAlarmService: pid=2321 uid=10069 gids={}
dalvikvm D Debugger has detached; object registry had 1 entries
szipinf D Initializing inflate state
BootAlarmService D oncreate()
BootAlarmService D alarm set for Thu Jan 17 08:03:00 CST 2013
MainActivity.java
package com.example.alarm_boot_test;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
#Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
}
}
BootAlarmService.java
package com.example.alarm_boot_test;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class BootAlarmService extends Service
{
private final String TAG = this.getClass ().getName ();
#Override
public void onCreate ()
{
Log.d (TAG, "oncreate()");
super.onCreate ();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId)
{
Log.d (TAG, "alarm_test: BootAlarmService.onStartCommand() Received start id " + startId + ": " + intent);
// if intent == null, service has been killed/restarted by system
if (intent != null)
createNotificationOnBoot();
else
Toast.makeText (getBaseContext (), "Intent was null in BootAlarmService.", Toast.LENGTH_LONG).show();
return START_STICKY;
}
private void createNotificationOnBoot ()
{
Intent inotify = new Intent(this , NotificationAlarmService.class);
inotify.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
AlarmManager amgr = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, inotify, 0);
// go off two mins from now
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, calendar.get (Calendar.MINUTE) + 2);
amgr.set (AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis (), pendingIntent);
Log.d (TAG, "alarm set for " + calendar.getTime ().toString ());
}
#Override
public IBinder onBind (Intent intent)
{
return null;
}
}
BootReceiver.java
package com.example.alarm_boot_test;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver
{
#Override
public void onReceive (final Context context, final Intent bootintent)
{
Intent i = new Intent ();
i.setAction ("com.example.alarm_boot_test.BootAlarmService");
context.startService (i);
}
}
NotificationAlarmService.java
package com.example.alarm_boot_test;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class NotificationAlarmService extends Service
{
private final String TAG = this.getClass().getName ();
#Override
public void onCreate ()
{
super.onCreate ();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId)
{
Log.d (TAG, "alarm_test: NotificationAlarmService.onStartCommand()");
if (intent != null)
createNotification ();
else
Toast.makeText (getBaseContext (), "Intent was null in NotificationAlarmService.", Toast.LENGTH_LONG).show();
return super.onStartCommand (intent, flags, startId);
}
private void createNotification()
{
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification(android.R.drawable.stat_sys_warning, "Note from AlarmService", System.currentTimeMillis());
Intent i = new Intent(this, ViewReminder.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
notification.setLatestEventInfo(this, "New Notification", "You have been notified by AlarmService", pendingIntent);
notificationManager.notify(10001, notification);
}
#Override
public IBinder onBind (Intent intent)
{
return null;
}
}
*activity_main.xml*
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Service started! Reboot!" />
</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alarm_boot_test"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="9" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<service android:name="com.example.alarm_boot_test.BootAlarmService" >
<intent-filter>
<action android:name="com.example.alarm_boot_test.BootAlarmService" >
</action>
</intent-filter>
</service>
<receiver android:name="com.example.alarm_boot_test.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" >
</action>
</intent-filter>
</receiver>
<service android:name="com.example.alarm_boot_test.NotificationAlarmService" >
</service>
<activity
android:name="com.example.alarm_boot_test.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
In case it helps someone else, I think I needed to check the passed Intent for NULL in the onStartCommand() method both in BootAlarmService.java as well as NotificationAlarmService.java. Only been testing it a couple days but it looks like the Intent is NULL if the system kills/restarts the service. Simply testing for this allowed me to create the notification only when the service is started at Boot-time (when the passed Intent is NOT null).