Please help me! I've tried for a week to fix my push notification demo but it still doesn't work! Device could not recieve messages after I quit my application.
This is the android element from application-descriptor.xml file (with Browser Key):
<android version="1.0" securityTest="mobile-securityTest">
<worklightSettings include="false"/>
<pushSender key="AIzaSyBmDTJw8IQAA1MmTF9MkSQUQXYxoFBKKtk" senderId="430560385547"/>
<security>
<encryptWebResources enabled="false"/>
<testWebResourcesChecksum enabled="false" ignoreFileExtensions="png, jpg, jpeg, gif, mp4, mp3"/>
<publicSigningKey>PushNotificationMyAppAndroid</publicSigningKey>
<packageName>com.MyApp</packageName>
</security>
<compressWebResources enabled="true"/>
</android>
This is main.js file:
$('#isSupportbtn').bind("click", isSupport);
$('#isSubscibedbtn').bind("click",isSubscribed);
$('#subscribebtn').bind("click",doSubscribe);
$('#unSubscribebtn').bind("click",doUnSubscribe);
function isSupport(){
var isSupport = false;
if(WL.Client.Push){
isSupport = WL.Client.Push.isPushSupported();
}
alert(isSupport);
}
function isSubscribed(){
var isSubscribed = false;
if(WL.Client.Push){
isSubscribed = WL.Client.Push.isSubscribed('myPush');
}
alert(isSubscribed);
}
if(WL.Client.Push){
WL.Client.Push.onReadyToSubscribe = function(){
$('#subscribebtn').removeAttr('disabled');
$('#unSubscribebtn').removeAttr('disabled');
WL.Client.Push.registerEventSourceCallback(
"myPush",
"AdapterAuth",
"pushEventSource",
pushNotificationReceived
);
};
}
function doSubscribe(){
WL.Client.Push.subscribe("myPush",{
onSuccess:function(){
alert("Success");
},
onFailure:function(){
alert("Fail");
}
});
}
function doUnSubscribe(){
WL.Client.Push.unsubscribe("myPush",{
onSuccess:function(){
alert("Success");
},
onFailure:function(){
alert("Fail");
}
});
}
function pushNotificationReceived(props, payloads){
alert("pushNotificationReceived invoked");
alert("props :: " + JSON.stringify(props));
alert("payload :: " + JSON.stringify(payload));
}
This is AdapterAuth-impl.js file:
function getUser() {
return {Data:[{username:'hcv',password:"123456",bagde:'3',message:'hi, Jane'},{username:'vi',password:'123',bagde:'3',message:'hi, Luffy'}]};
}
function onAuthRequired(headers,errorMessage){
errorMessage = errorMessage ? errorMessage:null;
return{
authRequired:true,
errorMessage:errorMessage
};
}
function submitAuth(username,password) {
var response = {Data:[{username:'hcv',password:"123456",bagde:'3',message:'hi, Jane'},{username:'vi',password:'123',bagde:'3',message:'hi, Luffy'}]};
for (var i=0;i<response.Data.length;i++){
if(username === response.Data[i].username && password === response.Data[i].password){
var Identity={
userId: username,
displayName: username,
attributes:{foo:"bar"}
};
WL.Server.setActiveUser("AuthLoginRealm", Identity);
return{
authRequired: false
};
}
}
return onAuthRequired(null,"Invalid login credentials");
}
function onLogout(){
WL.Server.setActiveUser("AuthLoginRealm", null);
WL.Logger.debug("Logged out");
}
WL.Server.createEventSource({
name:"pushEventSource",
poll:{
interval:60,
onPoll:"submitNotifications"
},
securityTest:"mobile-securityTest",
});
function submitNotifications() {
var response = {Data:[{username:'hcv',password:"123456",bagde:"3",message:"hi,Jane"},{username:"vi",password:"123",bagde:"1",message:"hi, Luffy"}]};
var messages = response.Data;
for(var i=0; i < messages.length;i++){
var userId = WL.Server.getActiveUser("AuthLoginRealm");
if(messages[i].username === userId){
var userSubscription = WL.Server.getUserNotificationSubscription("AdapterAuth.pushEventSource", messages[i].username);
if(userSubscription === null){
return {result:"no user was found:"+messages[i].username};
}
var notification = WL.Server.createDefaultNotification(messages[i].message, messages[i].bagde,{foo:'bar'});
WL.Logger.debug("submitNotification >> userId :: " + messages[i].username + ", text :: " + messages[i].message);
WL.Server.notifyAllDevices(userSubscription, notification);
WL.Logger.error("UserId"+messages[i].username+"not found");
}
}
}
This is error:
06-24 09:27:11.927: E/ActivityThread(3096): Activity com.MyApp.MyApp has leaked IntentReceiver com.google.android.gcm.GCMBroadcastReceiver#41c85190 that was originally registered here. Are you missing a call to unregisterReceiver()?
06-24 09:27:11.927: E/ActivityThread(3096): android.app.IntentReceiverLeaked: Activity com.MyApp.MyApp has leaked IntentReceiver com.google.android.gcm.GCMBroadcastReceiver#41c85190 that was originally registered here. Are you missing a call to unregisterReceiver()?
06-24 09:27:11.927: E/ActivityThread(3096): at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:763)
06-24 09:27:11.927: E/ActivityThread(3096): at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:567)
06-24 09:27:11.927: E/ActivityThread(3096): at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1055)
06-24 09:27:11.927: E/ActivityThread(3096): at android.app.ContextImpl.registerReceiver(ContextImpl.java:1042)
06-24 09:27:11.927: E/ActivityThread(3096): at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:348)
06-24 09:27:11.927: E/ActivityThread(3096): at com.google.android.gcm.GCMRegistrar.setRetryBroadcastReceiver(GCMRegistrar.java:293)
06-24 09:27:11.927: E/ActivityThread(3096): at com.google.android.gcm.GCMRegistrar.register(GCMRegistrar.java:215)
06-24 09:27:11.927: E/ActivityThread(3096): at com.worklight.androidgap.plugin.Push.subscribe(Push.java:331)
06-24 09:27:11.927: E/ActivityThread(3096): at com.worklight.androidgap.plugin.Push.access$400(Push.java:45)
06-24 09:27:11.927: E/ActivityThread(3096): at com.worklight.androidgap.plugin.Push$2.run(Push.java:233)
06-24 09:27:11.927: E/ActivityThread(3096): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
06-24 09:27:11.927: E/ActivityThread(3096): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
06-24 09:27:11.927: E/ActivityThread(3096): at java.lang.Thread.run(Thread.java:856)
I have tested your application. I managed to receive the push notification in all 3 scenarios: when the app is in the foreground, when the app is in the background and when the app is close.
I have 2 comments:
var response = {Data:[{username:'hcv',password:"123456",bagde:"3",message:"hi,Jane"},{username:"vi",password:"123",bagde:"1",message:"hi,Julian"}]};
Here, the badge should not be in double-quotes. Remove the quotes.
The submitNotifications function... I see that you are building everything inside of it - the notification text and the user to be sent to, but this is not how it is supposed to be(!).
This function is supposed to receive these: submitNotifications(userId,
notifivcationText)
So I don't know how you are sending the push notifications, it looks wrong to me.
Anyway, to get it to work I had to change
if( messages[i].username === userId){ ...
To:
if( true || messages[i].username === userId){ ...
I strongly suggest to re-read the Push Notifications training module.
Related
I'm trying to find out a way to encrypt, decrypt form data and store it in local files of the device using Cordova for building windows apps. for Android platform, we can easily do it using simple-crypto plugins. Just trying to find an alternative in windows. Thank you very much.
I'm trying to find out a way to encrypt, decrypt form data and store it in local files of the device using Cordova for building windows apps.
You can call WinRT API in Cordova codes directly, or you can wrap the codes into a custom plugin. Any way you will have to use Windows.Security.Cryptography.DataProtection to do the job.
Sample js codes:
if ((cordova.platformId == "windows")) {
var Cryptography = Windows.Security.Cryptography;
//Encrypt the msg string
function protectData(msg) {
var provider = new Cryptography.DataProtection.DataProtectionProvider("LOCAL=user");
var encoding = Cryptography.BinaryStringEncoding.utf8;
var buffMsg = Cryptography.CryptographicBuffer.convertStringToBinary(msg, encoding);
provider.protectAsync(buffMsg).done(function (buffData) {
//the encryption has succeeded
writeToFile(buffData);
});
}
//decrypt the buffProtected buffer
function unprotectData(buffProtected) {
var provider = new Cryptography.DataProtection.DataProtectionProvider("LOCAL=user");
var encoding = Cryptography.BinaryStringEncoding.utf8;
// Decrypt the protected message specified on input.
provider.unprotectAsync(buffProtected).then(function (data) {
var stringMsg = Cryptography.CryptographicBuffer.convertBinaryToString(Windows.Security.Cryptography.BinaryStringEncoding.utf8, data);
document.getElementById("divResult").innerText = stringMsg;
}, function (error) {
console.log(error);
});
}
function writeToFile(buffProtected) {
Windows.Storage.ApplicationData.current.localFolder.createFileAsync("test.txt", Windows.Storage.CreationCollisionOption.replaceExisting)
.then(function (storageFile) {
Windows.Storage.FileIO.writeBufferAsync(storageFile, buffProtected).done(function(){});
}, function (error) {
console.log(error);
})
}
function readFromFile() {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync("test.txt").then(function (storageFile) {
var abc = storageFile;
Windows.Storage.FileIO.readBufferAsync(storageFile).then(function (buffData) {
//decrypt the data
unprotectData(buffData);
});
}, function (error) {
console.log(error);
});
}
document.getElementById("btnEncrypt").onclick = function () {
var data = document.getElementById("txtData").value;
protectData(data);
}
document.getElementById("btnDecrypt").onclick = function () {
readFromFile(unprotectData)
}
}
And Html:
<input type="text" id="txtData" placeholder="ToProtected Data" />
<button id="btnEncrypt">Encrypt Data</button>
<button id="btnDecrypt">Decrypt Data</button>
<div id="divResult"></div>
I am fairly new to Angular 2, TypeScript and RxJS and I am creating a simple application that leverages the Salesforce Ajax Toolkit connections library.
I am trying to write a handler to catch when a token has expired any time a method from the connections library is called. I have created a service that essentially wraps the connections library to use observables. For example if we look at the insert function I have created my own wrapper function:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => {
// This does not work yet
if (result.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
this.refreshToken();
}
else {
observer.error(result);
}
}
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
});
}
I have another function that refreshes the access token:
public refreshToken(): void {
this.loginService.login().subscribe(
response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
},
error => {
}
);
}
I essentially want the original insert function to wait for refreshToken to complete. If it is successful I want to retry the same insert again, otherwise I want the original insert observable to call observer.error.
I've looked into retry and retryWhen, however I haven't been able to figure out how to implement it to wait for the refreshToken() function to complete. Any guidance or advice on this matter would be greatly appreciated. Thank you in advance.
The catch operator accepts a function which processes an error and the source Observable. This means that if you catch an error you can determine whether you want to resubscribe to the original source in the catch block:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => observer.error(result);
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
}).catch((err, source) => {
if (err.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
//This waits for the refresh to complete and then resubscribes
//to the source
//If the refresh errors then it will skip the resubscribe
return this.refreshToken().flatMapTo(source);
}
//Non-authentication error
return Observable.throw(err);
});
}
Then make your refreshToken function into something like so:
public refreshToken(): Observable<any> {
return this.loginService.login()
.tap(response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
});
}
does this get triggered again from the server containing a message?
"ecb":"window.onNotificationGCM"
I have this set up on the server
device_tokens = [], //create array for storing device tokens
retry_times = 4, //the number of times to retry sending the message if it failed
sender = new gcm.Sender('AIzaSyDpA0b2smrKyDUSaP0Cmz9hz4cQ19Rxn7U'), //create a new sender
message = new gcm.Message(); //create a new message
message.addData('title', 'Open Circles');
message.addData('message', req.query.message);
message.addData('sound', 'notification');
message.collapseKey = 'testing'; //grouping messages
message.delayWhileIdle = true; //delay sending while receiving device is offline
message.timeToLive = 3; //the number of seconds to keep the message on the server if the device is offline
device_tokens.push(val.deviceToken);
sender.send(message, device_tokens, retry_times, function(result){
console.log(result);
console.log('push sent to: ' + val.deviceToken);
});
So what I want to know is, once a server call is made will it trigger the notification on the front. What am I missing about this system?
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if (event.foreground) {
console.log('INLINE NOTIFICATION');
var my_media = new Media("/android_asset/www/" + event.soundname);
my_media.play();
} else {
if (event.coldstart) {
console.log('COLDSTART NOTIFICATION');
} else {
console.log('BACKGROUND NOTIFICATION');
}
}
navigator.notification.alert(event.payload.message);
console.log('MESSAGE -> MSG: ' + event.payload.message);
//Only works for GCM
console.log('MESSAGE -> MSGCNT: ' + event.payload.msgcnt);
//Only works on Amazon Fire OS
console.log('MESSAGE -> TIME: ' + event.payload.timeStamp);
break;
case 'error':
console.log('ERROR -> MSG:' + event.msg);
break;
default:
console.log('EVENT -> Unknown, an event was received and we do not know what it is');
break;
}
};
return {
register: function () {
var q = $q.defer();
if(ionic.Platform.isAndroid()){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"346007849782",
"ecb":"window.onNotificationGCM"
}
);
}else{
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"window.onNotificationAPN"
}
);
}
return q.promise;
}
}
update. Eventually my server spit back this: TypeError: Cannot read property 'processIncomingMessage' of undefined
It seems my google ID was not working. I created a new one and now it's sending push requests.
I'm just wondering on how to throw multiple Meteor.Errors for my validation fields like
throw new Meteor.Error('403','Invalid Input','username Id');
throw new Meteor.Error('403','Too Short','password Id');
and throw them at the same time to client.
I'd take an approach like this:
var errors = [];
if (/* password is too short */) {
errors.push("password is too short");
}
if (/* username is invalid */) {
errors.push("username is invalid");
}
// ...
if (errors.length > 0) {
throw new Meteor.Error(403, errors.join("; "));
}
Achieved what I want by making two empty arrays,pushing values if an error is detected, wrapping them up to throw to the client and iterating those values in the client's ERROR callback.
//SERVER
var reason=[],ids=[];
if(error){
reason.push('error details');
ids.push('id of the element');
}
if(reason.length!==0){
throw new Meteor.Error('403',reason,ids);
}
//CLIENT
Accounts.createUser({foo:bar,boo:baz}, function(error){
if(error){
_.each(error.details,function(index,element){
and there your client error code goes
});
}
});
I'm hoping to be able to customise the error objects that are passed to the client if an exception occurs on the server.
I'm using the 'then' function on the client to handle success and failure:
hub.server.login(username, password).then(function(result) {
// use 'result'
}, function(error) {
// use 'error'
});
If the login succeeds, 'result' is the return value of the Login method on the server. If the login fails, I throw an exception of 'CustomException'. This is an exception with a 'Code' property.
if (!IsValidLogin(username, password))
throw new CustomException { Code: "BADLOGIN", Message: "Invalid login details" };
If I have detailed exceptions enabled, the 'error' argument on the client is 'Invalid login details' - the Message property of the exception.
Is there any way I can selectively change the error result from a string to a complex object? i.e. if 'CustomException' is thrown in a hub method, return a {Code:[...], Message:[...]} object for the client-side fail handler?
This should demonstrate what I'd like to see on the client:
hub.server.login(username, password).then(function(userInfo) {
alert("Hello " + userInfo.Name);
}, function(err) {
if (err.Code === "BADLOGIN.USERNAME")
alert("Unrecognised user name");
else if (err.Code === "BADLOGIN.PASSWORD");
alert("Invalid password");
else
alert("Unknown error: " + err.Message);
});
(Note the 'Code' and 'Message' properties on 'err').
When you call MapHubs with EnabledDetailedErrors set to true as follows:
RouteTable.Routes.MapHubs(new HubConfiguration { EnableDetailedErrors = true });
you will receive your Exception's Message string as the parameter to your fail handler.
I see that you have already figured this out, but I'm including the server side code to enable detailed errors for anyone else who might find this question later.
Unfortunately there is no easy way to send a complex object to the fail handler.
You could do something like this though:
if (!IsValidUsername(username))
{
var customEx = new CustomException { Code: "BADLOGIN.USERNAME", Message: "Invalid login details" };
throw new Exception(JsonConvert.SerializeObject(customEx));
}
if (!IsValidPassword(username, password))
{
var customEx = new CustomException { Code: "BADLOGIN.PASSWORD", Message: "Invalid login details" };
throw new Exception(JsonConvert.SerializeObject(customEx));
}
Then on the client:
hub.server.login(username, password).then(function(userInfo) {
alert("Hello " + userInfo.Name);
}, function(errJson) {
var err = JSON.parse(errJson);
if (err.Code === "BADLOGIN.USERNAME")
alert("Unrecognised user name");
else if (err.Code === "BADLOGIN.PASSWORD");
alert("Invalid password");
else
alert("Unknown error: " + err.Message);
});
I know this is ugly, but it should work.