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.
Related
We have a requirement to show an acknoldgement screen after user share some texts on Messaging apps such as Whatsapp, Telegram, SMS etc..
We have been using the react-native-share library for this and it works fine on non Huawei devices. However, Huawei devices doesn't return success/failure when user share hence the app gives issues on Huawei devices.
Upon checking the react-native-share library internal codes, it seems the BroadcastReceiver onReceive method does not get triggered when user choose and app on HuaweiChooser.
I created the following sample code to simulate this and on Non-Huawei devices this code's onReceive method get triggered but not in Huawei devices.
Appreciate any help regarding this.
This is the Sample code
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button shareButton = findViewById(R.id.button);
shareButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent chooser;
IntentSender intentSender = MyReceiver.getIntentSender(getApplicationContext());
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
chooser = Intent.createChooser(sendIntent,"Sharing Rocks",intentSender);
chooser.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}else{
chooser = Intent.createChooser(sendIntent,"Sharing Rocks");
}
startActivityForResult(chooser, 1234);
}
});
}
}
Receiver.Java
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.IntentSender;
import android.widget.Toast;
public class MyReceiver extends BroadcastReceiver {
private static MyReceiver myReceiver;
private static final String EXTRA_RECEIVER_TOKEN = "receiver_token";
public static IntentSender getIntentSender(Context context){
String intentAction = context.getPackageName()+"_ACTION";
myReceiver = new MyReceiver();
context.registerReceiver(myReceiver, new IntentFilter(intentAction));
Intent intent = new Intent(intentAction);
intent.setPackage(context.getPackageName());
intent.putExtra(EXTRA_RECEIVER_TOKEN,myReceiver.hashCode());
PendingIntent callback = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return callback.getIntentSender();
}
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("//// Inside onReceive");
ComponentName target = intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT);
System.out.println("//// target : " + target);
}
}
Manifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lk.infinitx.huaweisharetest">
<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/Theme.HuaweiShareTest">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true"></receiver>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I change chooser title with "null" and my problem solved.
val chooser = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
Intent.createChooser(sendIntent, null, pendingIntent.intentSender)
} else {
Intent.createChooser(sendIntent, null)
}
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.
Not opening specific activity on notification click when the app is in background/not running
The notification-click starts specified activity only when the app is opened up and the notification-click is performed. If the app is in background/not running and the notification-click is performed, the application's MainActivity opens up. In short, it is like the app opens normally following the activity stack instead of opening the specified activity in the PendingIntent.
Firebase Instance Id Service:
package com.example.tamzid.pushnotification;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyAndroidFirebaseInstanceIdService extends
FirebaseInstanceIdService {
private static final String TAG = "MyAndroidFCMIIDService";
#Override
public void onTokenRefresh() {
//Get hold of the registration token
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
//Log the token
Log.d(TAG, "Refreshed token: " + refreshedToken);
}
private void sendRegistrationToServer(String token) {
//Implement this method if you want to store the token on your server
}
}
Firebase Message Service:
package com.example.tamzid.pushnotification;
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.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyAndroidFirebaseMsgService extends FirebaseMessagingService
{
private static final String TAG = "MyAndroidFCMService";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//Log data to Log Cat
Log.d(TAG, "From: " + remoteMessage.getFrom());
Log.d(TAG, "Notification Message Body: " +
remoteMessage.getNotification().getBody());
//create notification
createNotification(remoteMessage.getNotification().getBody());
}
private void createNotification( String messageBody) {
Intent intent = new Intent( this , ResultActivity. class );
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultIntent = PendingIntent.getActivity( this , 0,
intent,
PendingIntent.FLAG_ONE_SHOT);
Uri notificationSoundURI =
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mNotificationBuilder = new
NotificationCompat.Builder( this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Android Tutorial Point FCM Tutorial")
.setContentText(messageBody)
.setAutoCancel( true )
.setSound(notificationSoundURI)
.setContentIntent(resultIntent);
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, mNotificationBuilder.build());
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tamzid.pushnotification">
<application
android:name="android.support.multidex.MultiDexApplication"
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>
<activity android:name=".ResultActivity"></activity>
<service android:name=".MyAndroidFirebaseMsgService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".MyAndroidFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"
/>
</intent-filter>
</service>
</application>
</manifest>
Try using remoteMessage.getData() instead of remoteMessage.getNotification()
Use remoteMessage.getNotification(): if message contains a
notification payload.
Use remoteMessage.getData():if message contains
a data payload.
Update onMessageReceived() as below:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0)
{
//create notification
createNotification(remoteMessage.getData().toString());
}
}
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.
I'm trying to develop a Geofence App for Android, that barely have to notify me when enter/exit/dwell in/from/into a Geofence.
Despite all questions and answers I red, I'm still not able to figure out my problem.
I think something is wrong with my PendinIntent and/or IntentService.
Here what I've done so far.
MainActivity.java
package it.coppola.geofencedetecttest1;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks,OnConnectionFailedListener{
private GoogleApiClient mGoogleApiClient;
private GeofenceSampleReceiver mGeofenceReceiver;
private IntentFilter filter;
private Location mCurrentLocation;
private PendingIntent mPendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient=new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
filter=new IntentFilter();
filter.addAction("it.coppola.geofencedetecttest1.ACTION_GEOFENCES_ERROR");
filter.addAction("it.coppola.geofencedetecttest1.ACTION_GEOFENCES_SUCCESS");
mGeofenceReceiver=new GeofenceSampleReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(mGeofenceReceiver, filter);
}
#Override
protected void onResume(){
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mGeofenceReceiver, filter);
}
#Override
protected void onPause(){
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mGeofenceReceiver);
}
#Override
protected void onStop(){
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mGeofenceReceiver);
}
#Override
protected void onDestroy(){
super.onDestroy();
}
public void onConnectToLocation(View v){
//stuff that happens when i press a button. Here is mGoogleApiClient.connect()
}
public void onConnected(Bundle connectionHint){
mCurrentLocation= LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation != null){
TextView lan=(TextView)findViewById(R.id.txtLan);
lan.setText(String.valueOf(mCurrentLocation.getLatitude()));
TextView lon=(TextView)findViewById(R.id.txtLong);
lon.setText(String.valueOf(mCurrentLocation.getLongitude()));
Geofence mGeofence1=new Geofence.Builder()
.setRequestId("Geofence1")
.setCircularRegion(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), 5)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER|Geofence.GEOFENCE_TRANSITION_EXIT|Geofence.GEOFENCE_TRANSITION_DWELL)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setLoiteringDelay(1000)
.build();
mPendingIntent=requestPendingIntent();
GeofencingRequest mGeofenceRequest=new GeofencingRequest.Builder()
.addGeofence(mGeofence1)
.build();
LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, mGeofenceRequest, mPendingIntent);
}
else {
}
}
/**
* Define a Broadcast receiver that receives updates from connection listeners and
* the geofence transition service.
*/
public class GeofenceSampleReceiver extends BroadcastReceiver {
/*
* Define the required method for broadcast receivers
* This method is invoked when a broadcast Intent triggers the receiver
*/
#Override
public void onReceive(Context context, Intent intent) {
// Check the action code and determine what to do
String action = intent.getAction();
Log.d("miotag","ho ricevuto questo intent: "+action);
if (TextUtils.equals(action, "it.coppola.geofencedetecttest1.ACTION_GEOFENCES_ERROR")){
Log.d("miotag","ho ricevuto un errore dal Location Service");
}
else if (TextUtils.equals(action, "it.coppola.geofencedetecttest1.ACTION_GEOFENCES_SUCCESS")){
Log.d("miotag","intent ricevuto: OK");
}
}
}
private PendingIntent requestPendingIntent(){
if (null != mPendingIntent){
return mPendingIntent;
} else {
Intent intent=new Intent(this, GeofenceReceiverSample.class);
return PendingIntent.getService(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
}
public void stopIt(View v){
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, mPendingIntent);
Log.d("miotag","geofence rimossa");
mGoogleApiClient.disconnect();
}
}
I've omitted some line of code not of interest.
here is my IntentService
package it.coppola.geofencedetecttest1;
import java.util.List;
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.support.v4.app.NotificationCompat;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;
public class GeofenceReceiverSample extends IntentService{
public GeofenceReceiverSample(){
super("GeofenceReceiverSample");
}
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geoEvent=GeofencingEvent.fromIntent(intent);
if (geoEvent.hasError()) {
//todo error process
} else {
int transitionType = geoEvent.getGeofenceTransition();
if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER ||
transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggerList = geoEvent.getTriggeringGeofences();
for (Geofence geofence : triggerList) {
generateNotification(geofence.getRequestId(), "address you defined");
}
}
}
}
private void generateNotification(String locationId, String address) {
long when = System.currentTimeMillis();
Intent notifyIntent = new Intent(this, MainActivity.class);
notifyIntent.putExtra("id", locationId);
notifyIntent.putExtra("address", address);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(locationId)
.setContentText(address)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.setWhen(when);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify((int) when, builder.build());
}
}
I've just pasted this IntentService from enter link description here, with few change.
Right now, I want to focus on IntentService and its handler: so at the moment I'm not asking for Broadcast Receiver that will be trigged from IntentService after the notification. This is why in my MainActivity there isn't any BroadcastReceiver class declared
My AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.coppola.geofencedetecttest1"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
-->
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="it.coppola.geofencedetecttest1.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>
<service android:name=".GeofenceReceiverSample"
android:exported="false">
</service>
<receiver android:name="it.coppola.geofencedetecttest1.GeofenceSampleReceiver"
android:exported="false">
<intent-filter >
<action android:name="it.coppola.geofencedetecttest1.ACTION_RECEIVE_GEOFENCE"/>
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
Few more notes:
I really don't know why, but a couple of time the app actually works: my IntentService was fired up from Google Play Service intent and execute a previous version of the code. Weird enough, when I launchede again the same app without any change in the code, IntentService wasn't fired up.
I'm developing with my tablet/smartphone as emulator device: I tried to walk around but no luck at all.
Any help is highly appreciate.
Thanks.
Following the answer provided by ianhanniballake, I've done more tests with radius that space between 20-30-40 meters, and I found out that all works as intended. As he said, my problems was about the radius settings too small for GPS to recognize its own position.
Sincerely thanks.