I'm trying to integrate Firebase Cloud Messaging (I use this code, which is mirrored here) with my existing app.
Relevant codes:
service.php
$msg_id = $_POST['id'];
$title = $_POST['title'];
$content = $_POST['content'];
$msg_date = $_POST['date'];
$author = $_POST['author'];
// insert the message into DB
$sql = "INSERT INTO tbl_message(msg_id, title, content, msg_date, author) VALUES ('$msg_id','$title','$content','$msg_date','$author')";
if ($result = mysqli_query($db, $sql)){
$firebase = new Firebase();
$push = new Push();
$payload = array();
$payload['foo'] = '123';
$payload['bar'] = 'xyz';
$push->setTitle($title);
$push->setMessage($content);
$push->setAuthor($author);
$push->setDate($msg_date);
$push->setImage('');
$push->setPayload($payload);
$push->setIsBackground(FALSE);
$resp = '';
$response = '';
$resp = $push->getPush();
$response = $firebase->sendToTopic('global', $resp);
echo $response;
}
Firebase.php
<?php
class Firebase {
// sending push message to single user by firebase reg id
public function send($to, $message) {
$fields = array(
'to' => $to,
//'data' => $message,
'data' => array("message" => $message));
return $this->sendPushNotification($fields);
}
// Sending message to a topic by topic name
public function sendToTopic($to, $message) {
$fields = array(
'to' => '/topics/' . $to,
//'data' => $message,
'data' => array("message" => $message)
);
return $this->sendPushNotification($fields);
}
// sending push message to multiple users by firebase registration ids
public function sendMultiple($registration_ids, $message) {
$fields = array(
'to' => $registration_ids,
//'data' => $message,
'data' => array("message" => $message)
);
return $this->sendPushNotification($fields);
}
// function makes curl request to firebase servers
private function sendPushNotification($fields) {
require_once 'config.php';
// Set POST variables
$url = 'https://fcm.googleapis.com/fcm/send';
$headers = array(
'Authorization: key=' . FIREBASE_API_KEY,
'Content-Type: application/json'
);
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Disabling SSL Certificate support temporarly
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
// Execute post
$result = curl_exec($ch);
if ($result === FALSE) {
die('Curl failed: ' . curl_error($ch));
}
// Close connection
curl_close($ch);
return $result;
}
}
?>
MainActivity.java
package studio.emcorp.monitoringsiswa;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static final String TAG = MainActivity.class.getSimpleName();
private BroadcastReceiver mRegistrationBroadcastReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
;
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// checking for type intent filter
if (intent.getAction().equals(NotificationConfig.REGISTRATION_COMPLETE)) {
// gcm successfully registered
// now subscribe to `global` topic to receive app wide notifications
FirebaseMessaging.getInstance().subscribeToTopic(NotificationConfig.TOPIC_GLOBAL);
displayFirebaseRegId();
} else if (intent.getAction().equals(NotificationConfig.PUSH_NOTIFICATION)) {
// new push notification is received
String message = intent.getStringExtra("message");
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
//txtMessage.setText(message);
}
}
};
#Override
protected void onResume() {
super.onResume();
// register GCM registration complete receiver
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(NotificationConfig.REGISTRATION_COMPLETE));
// register new push message receiver
// by doing this, the activity will be notified each time a new message arrives
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(NotificationConfig.PUSH_NOTIFICATION));
// clear the notification area when the app is opened
NotificationUtil.clearNotifications(getApplicationContext());
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
}
Sending messages directly from Firebase Console works fine. Now the problem is sending messages via PHP script. Somewhow the server manages to send the message successfuly, e.g:
{"message_id":4732936739964848111}
, and the device receive it:
06-19 09:26:21.590 18604-19560/net.anta40.app.firebasetest
E/MyFirebaseMessagingService: From: /topics/global 06-19 09:26:21.590
18604-19560/net.anta40.app.firebasetest E/MyFirebaseMessagingService:
Data Payload:
{message={"data":{"image":"","is_background":false,"payload":{"bar":"xyz","foo":"123"},"title":"test","message":"mehehehe","timestamp":"2017-06-19
2:26:21"}}} 06-19 09:26:21.600 18604-19560/net.anta40.app.firebasetest
E/MyFirebaseMessagingService: push json:
{"message":{"data":{"timestamp":"2017-06-19
2:26:21","message":"mehehehe","payload":{"bar":"xyz","foo":"123"},"image":"","title":"test","is_background":false}}}
06-19 09:26:21.600 18604-19560/net.anta40.app.firebasetest
E/MyFirebaseMessagingService: Json Exception: No value for data 06-19
09:26:21.720 1098-1098/? D/wpa_supplicant: RX ctrl_iface -
hexdump(len=11): 53 49 47 4e 41 4c 5f 50 4f 4c 4c 06-19 09:26:21.720
1098-1098/? D/wpa_supplicant: wlan0: Control interface command
'SIGNAL_POLL'
but not displayed via notifications. What goes wrong here?
You are sending a data-only payload in your PHP:
public function send($to, $message) {
$fields = array(
'to' => $to,
//'data' => $message,
'data' => array("message" => $message));
return $this->sendPushNotification($fields);
}
Your other methods are also sending the same.
If you're going to use a notification-only payload, you could simply structure it like so:
public function send($to, $message) {
$fields = array(
'to' => $to,
'notification' => array("title" => $title,
"body" => $body));
return $this->sendPushNotification($fields);
}
The difference from sending a message from the Firebase Console is that messages from the console are considered as notification messages.
In Android, each message type is handled differently (see Handling Messages).
Some useful posts:
Issue with FCM notification
Can we push only data message from Firebase Notification console
Related
I am trying to resolve an azure marketplace subscription using the azure fulfilment api. I have followed the instructions on the Microsoft's SaaS fulfilment api's docs but I am not able to resolve the subscription using PHP.
I am able to get access_token and incidentally I am able to use the token and and use this together with the purchase identification token i get from azure portal when the subscriber is re-directed to the SaaS landing page, to get a successful json response when using postman.
I cannot achieve the same success when using PHP. I get a 403 error - Authorization is missing, incorrect or invalid. I am thinking that the query string bit of authorization parameter is malformed. This has nothing to do with privileges or permissions as I am able to get a successful output on Postman. Here is the code
<?php
use Microsoft\Graph\Graph;
use Microsoft\Graph\Http;
use Microsoft\Graph\Model;
use GuzzleHttp\Client;
class GraphHelper {
private static Client $tokenClient;
private static Client $tokenWebClient;
private static string $clientId = '';
private static string $tenantId = '';
private static string $clientSec = '';
private static string $graphUserScopes = '';
private static Graph $userClient;
private static string $userToken;
private static string $resolveToken;
private static string $subToken= '';
public static function initializeGraphForUserAuth(): void {
GraphHelper::$tokenClient = new Client();
GraphHelper::$clientId = $_ENV['CLIENT_ID'];
GraphHelper::$clientSec = $_ENV['CLIENT_SECRET'];
GraphHelper::$tenantId = $_ENV['TENANT_ID'];
GraphHelper::$graphUserScopes = $_ENV['GRAPH_USER_SCOPES'];
GraphHelper::$userClient = new Graph();
}
public static function getUserToken(): void {
//getting the access token
$accessCodeRequestUrl = 'https://login.microsoftonline.com/'.GraphHelper::$tenantId.'/oauth2/token';
$tokenRequestUrl = 'https://marketplaceapi.microsoft.com/api/saas/subscriptions/resolve?api-version=2018-08-31';
$subToken = $_SESSION['subToken'];
$tokenResponse = GraphHelper::$tokenClient->post($accessCodeRequestUrl, [
'form_params' => [
'client_id' => GraphHelper::$clientId,
'grant_type' => 'client_credentials',
'client_secret' => GraphHelper::$clientSec,
'resource' => '20e940b3-4c77-4b0b-9a53-9e16a1b010a7'
],
// These options are needed to enable getting
// the response body from a 4xx response
'http_errors' => false,
'curl' => [
CURLOPT_FAILONERROR => false
]
]);
if ($tokenResponse->getStatusCode() == 200) {
// Return the access_token
$responseBody = json_decode($tokenResponse->getBody()->getContents());
GraphHelper::$resolveToken = $responseBody->access_token;
$resolveAccessToken= $responseBody->access_token;
} else if ($tokenResponse->getStatusCode() == 400) {
// Check the error in the response body
$responseBody = json_decode($tokenResponse->getBody()->getContents());
if (isset($responseBody->error)) {
$error = $responseBody->error;
// authorization_pending means we should keep polling
if (strcmp($error, 'authorization_pending') != 0) {
throw new Exception('Token endpoint returned '.$error, 100);
}
}
}
//resolving the subscription
$resolveResponse = GraphHelper::$tokenClient->post($tokenRequestUrl, [
'form_params' => [
'content-type' => 'application/json',
'authorization' => 'Bearer '.$resolveAccessToken,
'x-ms-marketplace-token'=> $subToken
],
// These options are needed to enable getting
// the response body from a 4xx response
'http_errors' => false,
'curl' => [
CURLOPT_FAILONERROR => false
]
]);
//test whether there is a reponse
return $resolveResponse->getStatusCode(); // this returns a 403 - Authorization is missing, incorrect or invalid.
}
}
?>
I figured out that I needed to use cUrl for option to include authorization and other headers. Postman did this automatically hence the reason I was able to get results with postman and not with PHP. Eventually this code did it for me.
$subToken = rawurldecode($_SESSION['subToken']);
$ch = curl_init ();
curl_setopt ($ch, CURLOPT_URL, $tokenRequestUrl);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_HTTPHEADER, array ('Authorization: Bearer '.GraphHelper::$resolveToken,
'Content-type: application/json',
'X-ms-marketplace-token:'. $subToken));
curl_setopt($ch, CURLOPT_POST, true);
$results = json_decode (curl_exec ($ch), 1);
if (array_key_exists ('error', $results)){
echo ($results['error']);
die();
}
curl_close($ch);
return $results['subscriptionName'] ;
I found out that the endpoint was not accepting a get request. It threw an error of Subscription resolve not found","target":"subscriptionId","code":"EntityNotFound . However including a post option corrected that and no error was thrown after this. Again when decoding the token from the url - don't use urldecode() use rawurldecode () instead.
I'm using Firebase for push notifications. My PHP code is working fine. I'm getting success message, but not receiving the push notification in my Android app for single device and multi device. But using Firebase console for sending notification it's working fine. I got the notification on the Android device. Is there any server configuration that I need to add?
PHP Code:
$yourApiSecret = "AIzaSyDY";
$androidAppId = "traasasadad";
$data = array(
"tokens" => "AAAA_kFbSQ4:APA91bQuMV-nRuTnVNFg0HD2C9PBnWWad",
"notification" => "Hello World!"
);
$data_string = json_encode($data);
$ch = curl_init('https://push.ionic.io/api/v1/push');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'X-Ionic-Application-Id: '.$androidAppId,
'Content-Length: ' . strlen($data_string),
'Authorization: Basic '.base64_encode($yourApiSecret)
)
);
$result = curl_exec($ch);
var_dump($result);
Android Code:
package com.seven77Trades.notification;
/** * Created by ist on 21/3/17. */ 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; import
com.seven77Trades.HomeActivity; import com.seven77Trades.R;
public class FirebaseMsgService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/**
* Called when message is received.
*
* #param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// [START_EXCLUDE]
// There are two types of messages data messages and notification messages. Data messages are handled
// here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
// traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
// is in the foreground. When the app is in the background an automatically generated notification is displayed.
// When the user taps on the notification they are returned to the app. Messages containing both notification
// and data payloads are treated as notification messages. The Firebase console always sends notification
// messages. For more see:
// [END_EXCLUDE]
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be:
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());
/*Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getColor());
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getSound());
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getTag());
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getClickAction());*/
sendNotification(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.
}
// [END receive_message]
/**
* Create and show a simple notification containing the received FCM message.
*
* #param messageBody FCM message body received.
*/
private void sendNotification(String messageBody) {
Intent intent = new Intent(this, HomeActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_logo)
.setContentTitle("Firebase")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
} }
Output:
"{"multicast_id":8295856130292351869,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1492611205996022%0296efeff9fd7ecd"}]}"
I got the solution .finally I'm receiving notification in android device.When we are sending notification using API (php,java,python) that time android application getting this request in different method (WakefulBroadcastReceiver) and when we sending using fire base console then request comes different method (FirebaseMessagingService).
Here the BrackPullBroadCastReceiver:
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.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
public class GcmIntentService extends IntentService {
private Context context;
public GcmIntentService() {
super("GcmIntentService");
}
String imageUrl = "";
#Override
protected void onHandleIntent(Intent intent) {
context = this;
Bundle extras = intent.getExtras();
for (String key: extras.keySet())
{
Log.d (TAG, key + " is a key in the bundle");
Log.d(TAG, extras.get(key) + "");
}
}
}
I think there is some mistake with the target url you can try sending the notification like this:
$url = 'http://fcm.googleapis.com/fcm/send';
$fields =array(
"notification"=> array(
"title" => 'sometitle',
"body" => $message, //Can be any message you want to send
"icon" => $image,
"click_action" => "http://google.com"
),
"registration_ids"=> 'Your android app fcm token',
"data"=> array(
"data" => "something",
)
);
$fields = json_encode ( $fields );
$headers = array (
'Authorization: key=' . "your firebase app key",
'Content-Type: application/json'
);
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt ( $ch, CURLOPT_POST, true );
curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $fields );
$result = curl_exec ( $ch );
curl_close ( $ch );
Also you can pass custom data in the payload
I'm getting a notification only in one device that is set as the first token stored the table in mySQL DB and the notification is not sent to the rest of the token numbers. I tried a WHILE loop and stored the token numbers in an array, but it did not work.
Please suggest a solution. Thank you.
Here is my code:
<?php
require "init.php";
$message=$_POST['message'];
$title=$_POST['title'];
$path_to_fcm='https://fcm.googleapis.com/fcm/send';
$server_key="A*************************Q";
$sql="select token from fcm_info";
$result =mysqli_query($con,$sql);
$row=mysqli_fetch_row($result);
$key=$row[0];
$headers = array(
'Authorization:key=' .$server_key,
'Content-Type:application/json'
);
$fields =array('to'=>$key,
'notification'=>array('title'=>$title,'body'=>$message));
$payload =json_encode($fields);
$curl_session =curl_init();
curl_setopt($curl_session,CURLOPT_URL, $path_to_fcm);
curl_setopt($curl_session,CURLOPT_POST, true);
curl_setopt($curl_session,CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl_session,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl_session,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_session,CURLOPT_IPRESOLVE, CURLOPT_IPRESOLVE);
curl_setopt($curl_session,CURLOPT_POSTFIELDS, $payload);
$result=curl_exec($curl_session);
curl_close($curl_session);
mysqli_close($con);
?>
Use 'registration_ids' instead of 'to' and pass comma separated multiple registrations ids to use multicast in FCM. Final payload should be like:
{
"registration_ids":["id1","id2",...],
"priority" : "normal",
"data" : {
"title" : "Title",
"message" : "Message to be send",
"icon": "icon_path"
}
}
see https://developers.google.com/cloud-messaging/http-server-ref for more help
////////////////////// FCM START /////////////////////////
$path_to_fcm = "https://fcm.googleapis.com/fcm/send";
$server_key = "your_server_key";
$headers = array(
'Authorization:key=' . $server_key,
'Content-Type:application/json');
$keys = ["key_1", "key_2"];
$fields = array(
"registration_ids" => $keys,
"priority" => "normal",
'notification' => array(
'title' => "title of notification",
'body' => "your notification goes here"
)
);
$payload = json_encode($fields);
$curl_session = curl_init();
curl_setopt($curl_session, CURLOPT_URL, $path_to_fcm);
curl_setopt($curl_session, CURLOPT_POST, true);
curl_setopt($curl_session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_session, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($curl_session, CURLOPT_POSTFIELDS, $payload);
$curl_result = curl_exec($curl_session);
////////////////////// FCM END /////////////////////////
This works for me.
You need to cover the notification sending logic in method and then start the loop & call that method in each iterations pass token and message to the method.
"Please suggest a solution"
I would like to suggest using Services. You're most recommended to read the documention by Android Studio here.
There is a lot to perceive about Services, but at the moment I believe a snippet will be most helpful to you, here is a little code,
Create a class called HelloService
and paste the following code inside with the proper imports*
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "servicestarting",Toast.LENGTH_SHORT).show();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
"This is overhwleming" you might think to yourself. However it's but the contrary.
Example for Services + Firebase
Instead of pushing a message from Firebase, let's say you want to notify a user whenever a modification takes place in one of your databases
first, create databasereference earlier on the Oncreate
mDatabaseLike=FirebaseDatabase.getInstance().getReference().child("Likes");
Go to 'handleMessage Method' and add the following
#Override
public void handleMessage(Message msg) {
mDatabaseLike.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
notifyUserOfDBupdate()
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//stopSelf(msg.arg1);
}
}
Here is the notifyUserOfDBupdate method and how to notify a user
private void notifyUserOfDBupdate() {
//Intents
Intent Pdf_view = new Intent(this, //class to throw the user when they hit on notification\\.class);
PendingIntent pdf_view = PendingIntent.getActivity(this, 0, Pdf_view, 0);
//Notification Manager
NotificationManager nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
//The note
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Notification noti = new NotificationCompat.Builder(getApplicationContext())
.setTicker("TickerTitle")
.setContentTitle("content title")
.setSound(soundUri)
.setContentText("content text")
.setContentIntent(pdf_view).getNotification();
//Execution
noti.flags = Notification.FLAG_AUTO_CANCEL;
nm.notify(0, noti);
}
Now run your application once on your real device and a second time on an emulator. Once either one of two modifies your firebase database, the other will be notified instantly.
Modify whichever method you like inside the HandleMessage method. It will be eternal, not unless you make it killable.
kindest regards
I have a problem trying to integrate sendgrid library in Symfony2. We have copied the library inside our bundle and we have included it in a service. If this service is called by an action, the library it is running successfully.
This is the service:
use Symfony\Component\DependencyInjection\ContainerInterface;
require_once(__DIR__.'/SendgridPhp/sendgrid-php.php');
class MailerService extends \Twig_Extension {
protected $container;
private $mailer;
private $templating;
public function __construct(ContainerInterface $container,$mailer,$templating)
{
$this->container= $container;
$this->mailer = $mailer;
$this->templating=$templating;
}
public function sendEmail($to, $from, $subject, $body, $attachment = null)
{
$sendgrid = new \SendGrid($this->container->getParameter('sendgrid_user'), $this->container->getParameter('sendgrid_password'));
$email = new \SendGrid\Email();
$email->setFrom($from)
->setFromName('Name')
->setSubject($subject)
->addTo($to)
->setHtml($body, 'text/html');
$salida = $sendgrid->send($email );
}
}
The problem happens when we call this service from a symfony2 command.
$mailerService = $container->get('mailer.service');
$mailerService->sendEmail($user->getEmail(), $container->getParameter("sender_email"), 'Message', $body);
The error in namespace is the next:
PHP Fatal error: Class 'SendGrid\Email' not found in /var/www/SpainStartup/src/SpainStartup/CommunicationBundle/Services/MailerService.php on line 35
Should we do something special to load the library in command context ?
Thanks in advance
You'll need to autoload the library. The files for third-party code usually go in the vendor folder. You can add it to your composer.json in the require section:
{
"require": {
// Other dependencies...
"sendgrid/sendgrid": "2.0.5"
}
}
Remove what you "copied" in. Then run composer update wherever your composer.json file is located.
After this it should 'just work'.
Instead of using any sendgrid's library, I just worked upon the php example they provided on the api site. This stand-alone php code works flawlessly for me:
sendmail.php
<?php
function sendgridmail($from, $to, $subject, $message, $headers)
{
print_r('entering the function');
$url = 'https://api.sendgrid.com/';
$user='shinujacob';
$pass='mypassword';
$params = array(
'api_user' => $user,
'api_key' => $pass,
'to' => $to,
'subject' => $subject,
'html' => '',
'text' => $message,
'from' => $from,
);
$request = $url.'api/mail.send.json';
// Generate curl request
$session = curl_init($request);
// Tell curl to use HTTP POST
curl_setopt ($session, CURLOPT_POST, true);
// Tell curl that this is the body of the POST
curl_setopt ($session, CURLOPT_POSTFIELDS, $params);
// Tell curl not to return headers, but do return the response
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
//print_r('obtaining the response');
// obtain response
$response = curl_exec($session);
print_r('closing curl session');
curl_close($session);
// print everything out
//print_r($response);
}
//only for testing:
/*$to = 'shinujacobrocks#yahoo.com';
$subject = 'Testemail';
$message = 'It works!!';
echo 'To is: ' + $to;
#wp_mail( $to, $subject, $message, array() );
sendgridmail($to, $subject, $message, array());
print_r('Just sent!');*/
I am able to get the device id and save it to my database, and when something happens, I try to send the push notification but it does not get delivered to the phone. Here is what I do in my PHP:
$url = 'https://android.googleapis.com/gcm/send';
$device_ids = array( $device_id );
$headers = array('Authorization: key=' . 'my_api_key',
'Content-Type: application/json');
$t_data = array();
$t_data['message'] = 'Someone commented on your business.';
$t_json = array( 'registration_ids' => $device_ids , 'data' => $t_data );
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Authorization: key=my_id', 'Content-Type: application/json' ) );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode( $t_json ) );
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);
if ($result === FALSE)
{
die('Curl failed: ' . curl_error($ch));
}
curl_close($ch);
and here is the result I get from the curl_exec call:
{"multicast_id":8714083978034301091,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1350807053347963%9aab4bd8f9fd7ecd"}]}
One thing I am wondering is whether I have to do something extra in the app like write my own Reciever class?
Thanks!
EDIT:
Here is my GCMIntentService class:
package com.problemio;
import static com.google.android.gcm.GCMConstants.ERROR_SERVICE_NOT_AVAILABLE;
import static com.google.android.gcm.GCMConstants.EXTRA_ERROR;
import static com.google.android.gcm.GCMConstants.EXTRA_REGISTRATION_ID;
import static com.google.android.gcm.GCMConstants.EXTRA_SPECIAL_MESSAGE;
import static com.google.android.gcm.GCMConstants.EXTRA_TOTAL_DELETED;
import static com.google.android.gcm.GCMConstants.EXTRA_UNREGISTERED;
import static com.google.android.gcm.GCMConstants.INTENT_FROM_GCM_LIBRARY_RETRY;
import static com.google.android.gcm.GCMConstants.INTENT_FROM_GCM_MESSAGE;
import static com.google.android.gcm.GCMConstants.INTENT_FROM_GCM_REGISTRATION_CALLBACK;
import static com.google.android.gcm.GCMConstants.VALUE_DELETED_MESSAGES;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.google.android.gcm.GCMBaseIntentService;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;
import utils.GCMConstants;
public class GCMIntentService extends GCMBaseIntentService
{
public GCMIntentService()
{
super(ProblemioActivity.SENDER_ID);
}
#Override
protected void onRegistered(Context ctxt, String regId) {
Log.d(getClass().getSimpleName(), "onRegistered: " + regId);
Toast.makeText(this, regId, Toast.LENGTH_LONG).show();
}
#Override
protected void onUnregistered(Context ctxt, String regId) {
Log.d(getClass().getSimpleName(), "onUnregistered: " + regId);
}
#Override
protected void onMessage(Context ctxt, Intent message) {
Bundle extras=message.getExtras();
for (String key : extras.keySet()) {
Log.d(getClass().getSimpleName(),
String.format("onMessage: %s=%s", key,
extras.getString(key)));
}
}
#Override
protected void onError(Context ctxt, String errorMsg) {
Log.d(getClass().getSimpleName(), "onError: " + errorMsg);
}
#Override
protected boolean onRecoverableError(Context ctxt, String errorMsg) {
Log.d(getClass().getSimpleName(), "onRecoverableError: " + errorMsg);
return(true);
}
}
UPDATE:
Looking at LogCat, it turned out that the message is getting to the device. But the device is not displaying the push notification for some reason.
From the response it seems that the message is delivered. On Android you should have a GCMIntentService class that extends GCMBaseIntentService, to receive the message on the device. You should check the gcm-demo-client that comes in the SDK samples for a good approach on how to implement this on the app. There you only need set the SENDER_ID (your google proyect number) in the CommonUtilities class to receive messages from your server.
More info here.
To generate the notification on the GCMIntentService you can use:
//Issues a notification to inform the user that server has sent a message.
private static void generateNotification(Context context, String message, String title,) {
int icon = R.drawable.logo;
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, AnActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Notification notification = new NotificationCompat.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent)
.setSmallIcon(icon)
.setLights(Color.YELLOW, 1, 2)
.setAutoCancel(true)
.setSound(defaultSound)
.build();
notificationManager.notify(0, notification);
}
Have you also registered the receiver on the manifest? Under the application tag?
<!--
BroadcastReceiver that will receive intents from GCM
services and handle them to the custom IntentService.
The com.google.android.c2dm.permission.SEND permission is necessary
so only GCM services can send data messages for the app.
-->
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.google.android.gcm.demo.app" />
</intent-filter>
</receiver>
<!--
Application-specific subclass of GCMBaseIntentService that will
handle received messages.
By default, it must be named .GCMIntentService, unless the
application uses a custom BroadcastReceiver that redefines its name.
-->
<service android:name=".GCMIntentService" />
You only need a collapseKey if you are planning to have your messages overwrite the previous message of that type. So if you are sending a message that the app needs to sync you can give it a collapse key so it will only send 1 sync message. The official docs describe how to use it.
While sending notification from GCM Server, which url to be used?
https://android.googleapis.com/gcm/send or
https://gcm-http.googleapis.com/gcm/send