Who's using what for Push Notifications?

I’m using GCM and that is not how you get the installation ID. It is passed through to the app when it is installed using something like this:

[lua]

function onNotification(event)
    print(" – main - onNotification(event)")
    
    if event.type == “remoteRegistration” then
        mojoData.PTOKEN = event.token    – Save off the installation token (push id)
–        native.showAlert( “remoteRegistration”, event.token, { “OK” } )
        print(" – saved token info: “, event.token)  
    elseif event.type == “remote” then
–        native.showAlert( “localNet Notification”, Json.encode( event ), { “OK” } )
        print(” – localNet Notification", Json.encode( event ) )               
        if( event.applicationState == “active” ) then
            native.setProperty(“applicationIconBadgeNumber”, 0)             – clear out list of notifications from notification center…
 
            native.showAlert( “localNet Notification”, Json.encode( event.alert ), { “OK” } )           
        end        
    end
    
    return true
end

– Tried adding the listener in systemListener(), but no go, so initializing right in main, like corona docs show – works here :confused:

Runtime:addEventListener( “notification”, onNotification ) 

[/lua]

Argh… The website never likes my code blocks :/  Using Decoda editor, and mebbe it doesn’t use cr/lf or something :confused:

hi, mpappas, I actually got a little hung up on this last night and today. Are you able to get your Android’s installation id using the onNotification listener? Somehow, I’m not able to. Would you be able to share your config and settings file with Android permissions? I thought I had this working a while ago but on my Samsung II it’s not. The sample GooglePushNotification app doesn’t register either. 

thanks,

Jen

Yes, the installation id (called a token in the docs) comes through just fine. I believe it not only comes to my app on installation, but that the corona sdk send it every launch of my app (I have some code that checks if it was already sent to the server, so I think that’s why I added it way back when). Anyways:

Dunno if they will appear correctly, but here they are:

Config:

[lua]

application =
{
    content =
    {
            width = 640,
            height = 960,
            scale = “letterbox”,
            fps = 60,            
    },
        
    notification =
    {
        iphone =
        {
            types =
            {
                “badge”, “sound”, “alert”
            }
        },
         google =
        {
            projectNumber = “261275118095”,
        },        
    },
        
}

[/lua]

build.settings:

[lua]

settings =
{

     android =
     {
        largeHeap = true,
     
         permissions =
        {
        { name = “.permission.C2D_MESSAGE”, protectionLevel = “signature” },
        },     
        usesPermissions =
        {
             “android.permission.INTERNET”,
             “android.permission.ACCESS_FINE_LOCATION”,
             “android.permission.ACCESS_COARSE_LOCATION”,
             “android.permission.ACCESS_LOCATION_EXTRA_COMMANDS”,
             “android.permission.WRITE_EXTERNAL_STORAGE”,
             “com.android.vending.BILLING”,                
             “android.permission.CAMERA”,  
             “android.permission.CALL_PHONE”,
             “android.permission.RECEIVE_BOOT_COMPLETED”,          
             “android.permission.GET_ACCOUNTS”,
             “com.google.android.c2dm.permission.RECEIVE”,
             “.permission.C2D_MESSAGE”,
             “android.permission.RECORD_AUDIO”,       
        },
        usesFeatures =
        {
            { name = “android.hardware.camera”, required = false },
            { name = “android.hardware.camera.front”, required = false },                 
            { name = “android.hardware.telephony”, required = false },                                        
        },        
    },
    
    iphone =
    {    
        components = {},
        
        plist =
        {
            UIPrerenderedIcon = true,
            UIApplicationExitsOnSuspend = false,

            CFBundleIconFile = “Icon.png”,
            CFBundleIconFiles = {
                    “Icon.png” ,
                    “Icon@2x.png” ,
                    “Icon-72.png”,
            },
            
            FacebookAppID = “423650107852131”,            
            CFBundleURLTypes =
            {
                {
                CFBundleURLSchemes =
                    {
                    “fb424050273855136”,
                    }
                }
            },
        },
    },            
}
 

[/lua]

hi, in the interest of science, I’ll put a theoretical fix up here and see if anyone can make it work. My problem is probably my testing environment. I have an un-activated Samsung II and a loaner Android that’s also not on a cell network, and I have a feeling that is stopping me getting the deviceID. That being said, I would think that the following would work (inspired by a post on the Parse forum).

  1. Install Parse Cloud Code like thus: https://www.parse.com/docs/cloud_code_guide. It will install some files on your local, including one called main.js

  2. In Main.js, add the following and deploy it up to your app’s cloud code:

[lua]Parse.Cloud.define(“sendPush”,function(request,response){

      Parse.Cloud.httpRequest({

      method: ‘POST’,

      url: ‘https://android.googleapis.com/gcm/send’,

      headers: {

        ‘Authorization’: ‘key=’ + ‘my_Google_API_Key’,

        ‘Content-Type’: ‘application/json’

      },

      body: {

        registration_ids: request.params.deviceId,

        data: “testing push notification on Android”

      },

      success: function(httpResponse) {

        console.log(“success”);

      },

      error: function(httpResponse) {

        console.log(‘GCM Request failed’ + JSON.stringify(httpResponse));

      }

    

    });

});[/lua]

  1. When you want to invoke the above code from your app, call it like this (you can use a REST API call to call any Parse cloud code function that you stick in min.'s:

–set the globals

APPID = ‘my_Parse_API_Key’

RESTAPIKEY = ‘my_Parse_REST_API_Key’

–pass through the device token you got back from GCM

[lua]

local function sendPush(dt)

        local function sendPushListener()

            print(“sent”)

        end

        headers = {}

        headers[“X-Parse-Application-Id”] = APPID

        headers[“X-Parse-REST-API-Key”] = RESTAPIKEY

        headers[“Content-Type”] = “application/json”

        local params = {}

        commands_json =

             {

                [“deviceId”] = dt

             }        

        postData = json.encode(commands_json)

        

        local params = {}

        params.headers = headers

        params.body = postData

        network.request( “https://api.parse.com/1/functions/sendPush”,“POST”,sendPushListener,params)

       

end[/lua]

This is all theoretical as my devices right now can’t get to first base (e.g. get the deviceID). But if someone else would like to run with this I think it would work. Look forward to your trials!

best,

Jen

Hi Jen, I just tested it real quick since its late here. Can look closer at it again in the morning.

My main question is where we are registering the device ID with Parse? Seems like the deployed function is registering on GCM only?

I got an error, but I will verify my test in the morning though, I was doing this hastily, might have missed something. So please someone else test too.  But I do know it is calling that function in the cloud because before adding that I got a ‘no function’ error.

My result from network request on device:

I/Corona (19568): Parse\_response = { I/Corona (19568): | responseHeaders = { I/Corona (19568): | | Access-Control-Allow-Origin = "\*", I/Corona (19568): | | Content-Length = "51", I/Corona (19568): | | X-UA-Compatible = "IE=Edge,chrome=1", I/Corona (19568): | | Date = "Thu, 20 Jun 2013 14:19:41 GMT", I/Corona (19568): | | Set-Cookie = "\_parse\_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlMzNlOTJjMjIzYTA3N2VlNmFmM2I3NWE0NWY1OTJjMDk%3D--d043537614b38524d8b9bada3beb9d69c5ec78bf; domain=.parse.com; path=/; expires=Sat, 20-Jul-2013 14:19:41 GMT; secure; HttpOnly", I/Corona (19568): | | Access-Control-Request-Method = "\*", I/Corona (19568): | | X-Runtime = "0.171536", I/Corona (19568): | | X-Android-Sent-Millis = "1371737981559", I/Corona (19568): | | Connection = "keep-alive", I/Corona (19568): | | Cache-Control = "no-cache", I/Corona (19568): | | X-Android-Received-Millis = "1371737981952", I/Corona (19568): | | Status = "400 Bad Request", I/Corona (19568): | | Content-Type = "application/json; charset=utf-8", I/Corona (19568): | | HTTP-STATUS-LINE = "HTTP/1.1 400 Bad Request", I/Corona (19568): | | Server = "nginx/1.2.2", I/Corona (19568): | }, I/Corona (19568): | responseType = "text", I/Corona (19568): | phase = "ended", I/Corona (19568): | bytesEstimated = 51, I/Corona (19568): | response = "{"code":141,"error":"success/error was not called"}", I/Corona (19568): | name = "networkRequest", I/Corona (19568): | bytesTransferred = 51, I/Corona (19568): | status = 400, I/Corona (19568): | url = "https://api.parse.com/1/functions/sendPush", I/Corona (19568): | isError = false, I/Corona (19568): | requestId = "false", I/Corona (19568): },  

ah yes, to register your device with parse, I put in the onNotification listener the following:

[lua]

if event.token ~= nil then

       registerParseDevice(event.token)

else

       print(“no token returned, too bad”)

end

[/lua]

and that function is:

[lua]

local function registerParseDevice(deviceToken)

    

    local environment = system.getInfo( “platformName” )

    if environment == “Android” then 

        deviceType = “android”

    else

        deviceType = “ios”

    end

     

      dt = deviceToken

      print(deviceType)

      print(dt)

      local function parseNetwork

deleted

ah yes, to register your device with parse, I put in the onNotification listener the following:

[lua]

if event.token ~= nil then

       registerParseDevice(event.token)

else

       print(“no token returned, too bad”)

end

[/lua]

and that function is:

[lua]

local function registerParseDevice(deviceToken)

    

    local environment = system.getInfo( “platformName” )

    if environment == “Android” then 

        deviceType = “android”

    else

        deviceType = “ios”

    end

     

      dt = deviceToken

      print(deviceType)

      print(dt)

      local function parseNetworkListener(event)

        print(event.response)        

      end

        headers = {}

        headers[“X-Parse-Application-Id”] = APPID

        headers[“X-Parse-REST-API-Key”] = RESTAPIKEY

        headers[“Content-Type”] = “application/json”

–[[I always put my user into a channel so that I can target them. Note, if you want scheduled pushes, you also need to send timeZone, formatted like ‘America/New\_York’. There’s a google database you can use to input lat/long to get this info]]–

        commands_json =

            {

             [“deviceType”] = deviceType,

             [“deviceToken”] = dt,

             [“channels”] = {“S”…dt}                 

            }        

        postData = json.encode(commands_json)

        

        data = “”

        local params = {}

        params.headers = headers

        params.body = postData

        network.request( “https://api.parse.com/1/installations” ,“POST”, parseNetworkListener,  params)

    

end

[/lua]

I think that is same as doing this Curl command I got from the docs. It can be pasted this into terminal after putting in app and api key (you can have any value in deviceID, same error in any case):

curl -X POST \ -H "X-Parse-Application-Id: 123abc" \ -H "X-Parse-REST-API-Key: 123abc" \ -H "Content-Type: application/json" \ -d '{ "deviceType": "android", "deviceToken": "123", "channels": [""] }' \ https://api.parse.com/1/installations

This gives error: {“code”:114,“error”:“deviceToken may only be set if deviceType is ‘ios’”}  

If you put ios in deviceType and valid ID it works.

my bad. Per the docs

https://www.parse.com/docs/rest#installations

You need to use installationId for Androids

  • deviceType is a required string field that must be set to either “ios” or “android”. It may not be changed once the object is created.
  • installationId is a Parse-generated string identifier that is required for devices with a deviceType of “android” and optional for devices with a deviceType of “ios”. It may not be changed once the object is created and must be unique across all of an app’s installations.
  • deviceToken is an Apple-generated string identifier that is required for devices with a deviceType of “ios”. It may not be changed once the object is created and must be unique across all of an app’s installations.
  • badge is a number field representing the last known application badge for iOS installations.
  • timeZone is a string field representing the system time zone of the device running this installation.
  • channels is an optional array of strings representing the subscriptions of this installation object.

A lot of replies since my last answer :stuck_out_tongue_winking_eye:
It is clear that we can’t use (right now) Parse on Android for pushing because of the installationId (if we don’t have an enterprise license).

I saw jen that you cannot register to GCM and push with the GooglePushNotification example ?

Because I tried it and I got no problem.

I did not make any progress on pushing because I am developing other things. But as I said before, I think that scheduled pushs could be done with GCM registration and Google App Engine (cron jobs + REST Api calls)/Heroku.

Emmanuel, I’m not sure that your above statement is correct. My phone config is just not talking to Google, but if you can get your InstallationID, pass it to parse like I do above and see if you can get the cloud code to fire.

Would love to see if someone can make this work.

thanks everyone for all your help! :slight_smile:

J

Jen, when you say installation ID, do you mean it should be same as device ID?

I tried that and: I get the error “Invalid installation ID”

I would think Its probably something generated by the Android Parse module? 

hi, I had thought that it’s the event.token that is returned by GCM. You pass that to Parse via InstallationId in the json params. Am I wrong? One question I have is whether you really need to register with Parse if you just use the cloud code. You get the InstallationId back from GCM and pass that thru to the cloud code. Did that throw an error?

thanks,

J

According to this you need Parse class natively to generate an installation ID, I think we can safely assume its not the same as deviceID: https://parse.com/questions/how-can-i-get-or-create-the-installationid-string-for-rest-api-pushnotifications

InstallationId has nothing to do with GCM … It is a created field by Parse.

So the event.token that you get back from GCM you just send straight to Parse cloud code, you don’t need to use it to do any Parse installation of your device because Parse isn’t the one sending the push, the cloud code takes care of it via a call back to GCM. 

Not sure I understand, but you can probably send a push with the cloud code using a network request but the device wont be listed under the installation or user section on Parse, so there is not much point I think. 

It would be a setup as Emmanuel has described it, except with Parse instead of Heroku as the external server and you need a cron job to fire it on a schedule. It’s a shame Parse doesn’t have a good way to schedule repeating jobs (you need an external cron job). :frowning:

I can see that we (me, jonjonsson and jen.looper) are still looking for a way to push on Android :stuck_out_tongue_winking_eye:
 

I have found something interesting :

http://www.coronalabs.com/blog/2012/12/25/corona-holiday-gifts-android-push-and-more/

So we can use GCM and depends on what we need :

  • we can directly send a push from our mobile application

  • we can setup our own server (in Google App Engine for example) to perform scheduled tasks / pushs