Xamarin Forms WinPhone 8.1 Silverlight WNS push notifications - push-notification

I'm attempting to configure my app to use WNS instead of MPNS (I am using Xamarin Forms and have a Win Phone 8.1 Silverlight project with an Azure Notification hub for the back end), for that I updated my code to use the the mobile service to register the phone for push notifications and changed the Notification Service in the WMAppManifest.xml to WNS. After implementing these changes when I check the phones registration through azure it says its MPNS. Below are some screen captures of my configuration and code snippets of how im registering the app.
WMAppManifest.xml
Package.appxmanifest
NotificationManager Code
public class PushNotificationManager : IPushNotificationManager
{
private PushNotificationChannel channel;
public PushNotificationManager() { }
public static MobileServiceClient MobileService = new MobileServiceClient(Utilities.Constants.ApplicationURL, Utilities.Constants.ApplicationKey);
public async Task RegisterDevice()
{
try
{
channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
channel.PushNotificationReceived += Channel_PushNotificationReceived;
await this.RegisterWinDevice(channel.Uri);
NotificationTask.UnregisterBackgroundTask();
NotificationTask.RegisterBackgroundTask();
}
catch (Exception ex)
{
throw ex;
}
}
protected void Channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
try
{
//Create notification
}
catch (Exception ex)
{
throw ex;
}
}
public async Task UnregisterDevice()
{
if(channel != null)
{
channel.Close();
}
await MobileService.GetPush().UnregisterNativeAsync();
}
private async Task RegisterWinDevice(string channelUri)
{
try
{
var tags = new List<string>() { };
User user = LocalStorage.GetUserInfo();
tags.Add(user.Id.ToString());
await MobileService.GetPush().RegisterNativeAsync(channelUri, tags.ToArray());
}
catch (Exception ex)
{
throw ex;
}
}
private void CreateNotification(string title, string message)
{
//Show Toast
}
}
In azure I have set the windows Package SID and client secret. I also have unauthenticated push notifications enabled (Although from my understanding this is for MPNS).
And finally here's a screen capture of how it registers with the following code:
If anyone has any idea how to get it to properly register to WNS I'd greatly appreciate the help. Thanks!

Just an update as to how I resolved my problem in case anyone comes across this. I had to switch my winphone project to a non silverlight application (I'm guessing its not supported with this version). Once I did this everything started working correctly.

Related

PushSharp 4.0.10.0: HTTP/2-based Apple Push Notification service (APNs)

We use PushSharp 4.0.10 to send iOS Push Notifications:
https://github.com/Redth/PushSharp
Recently we recieved this email from Apple Developer:
"If you still send push notifications with the legacy binary protocol, it's time to update to the HTTP/2-based Apple Push Notification service (APNs) provider API. You'll be able to take advantage of great features, such as authentication with a JSON Web Token, improved error messaging, and per-notification feedback.
To give you additional time to prepare, the deadline to upgrade to the APNs provider API has been extended to March 31, 2021. We recommend upgrading as soon as possible, as APNs will no longer support the legacy binary protocol after this date."
My question is: Will PushSharp 4.0.10 still work after March 31, 2021?
There is a discussion about this but the thread was closed. But there are still some suggestions on this thread that you might want to try.
The Apple Push Notification service (APNs) will no longer support the legacy binary protocol as of November 2020
https://github.com/Redth/PushSharp/issues/923
**
EDIT - 25th March 2021
The deadline is close and #Ashita Shah asked some code snippet so I hope the following can save your time.
Add the following class dotAPNSService to your project. You can customise this structure according to your needs. Also I didn't focus the best of best coding C# standards when implementing my own push notification service. You can implement LINQ, Tasks async etc. I tested this dotAPNS library and it works perfectly fine. For Android you can still use PushSharp.
Before you implement the dotAPNSService helper class, get the following from your Apple developer account. The ApnsJwtOptions values should be:
BundleId - your app’s bundle ID. Should not include specific topics (i.e. com.myapp but not com.myapp.voip).
CertFilePath - path to the .p8 certificate you have downloaded from the Developer Center.
KeyId - The 10-character Key ID you obtained from your developer account
TeamId - The 10-character Team ID you use for developing your company’s apps. Obtain this value from your developer account.
public class dotAPNSService : IDisposable
{
public event EventHandler OnTokenExpiredHandler;
private ApnsJwtOptions options = null;
public dotAPNSService()
{
options = new ApnsJwtOptions()
{
BundleId = "com.xx.xxxx",
CertFilePath = "../../certificate.p8",
KeyId = "The_Key_Id",
TeamId = "The_Team_Id"
};
}
public void SendNotifications(String[] deviceTokens, String title, String body)
{
if (deviceTokens == null || deviceTokens.Length <= 0)
{
return;
}
if (String.IsNullOrEmpty(title))
{
return;
}
if (String.IsNullOrEmpty(body))
{
return;
}
// once you've gathered all the information needed and created an options instance, it's time to call
var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);
// start the process
foreach (String deviceToken in deviceTokens)
{
var push = new ApplePush(ApplePushType.Alert)
.AddAlert(title, body)
.AddToken(deviceToken);
Send(apns, push, deviceToken);
}
}
public void SendSilentNotifications(String[] deviceTokens)
{
try
{
if (deviceTokens == null || deviceTokens.Length <= 0)
{
return;
}
// once you've gathered all the information needed and created an options instance, it's time to call
var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);
// start the process
foreach (String deviceToken in deviceTokens)
{
var push = new ApplePush(ApplePushType.Background)
.AddContentAvailable()
.AddToken(deviceToken);
Send(apns, push, deviceToken);
}
}
finally
{
}
}
private void Send(ApnsClient apns, ApplePush push, String deviceToken)
{
try
{
var response = apns.SendAsync(push);
if (response.Result.Reason == ApnsResponseReason.Success)
{
// the notification has been sent!
}
else
{
Boolean removeToken = false;
switch (response.Result.Reason)
{
case ApnsResponseReason.BadDeviceToken:
removeToken = true;
break;
case ApnsResponseReason.TooManyRequests:
break;
}
// remove the token from database?
if (removeToken)
OnTokenExpired(new ExpiredTokenEventArgs(deviceToken));
}
}
catch (TaskCanceledException)
{
// ERROR - HTTP request timed out, you can use the deviceToken to log the error
}
catch (HttpRequestException ex)
{
// ERROR - HTTP request failed, you can use the deviceToken to log the error
}
}
protected virtual void OnTokenExpired(ExpiredTokenEventArgs args)
{
try
{
EventHandler handler = OnTokenExpiredHandler;
if (handler != null)
{
ISynchronizeInvoke target = handler.Target as ISynchronizeInvoke;
if (target != null && target.InvokeRequired)
target.Invoke(handler, new object[] { this, args });
else
handler(this, args);
}
}
catch (Exception ex)
{
}
}
}
These are the namespaces of the dotAPNSService helper class:
using System;
using System.ComponentModel;
using System.Net.Http;
using System.Threading.Tasks;
using dotAPNS;
In order to use the dotAPNSService helper on your project just pull the tokens from the database and then pass them to it. For instance, to send silent notifications:
public void SendScheduledSilentNotifications()
{
try
{
IList<User> users = _accountService.GetUsers(true);
if (users != null && users.Count > 0)
{
List<String> deviceTokens = new List<String>();
foreach (User user in users)
{
if (!String.IsNullOrEmpty(user.DeviceToken))
deviceTokens.Add(user.DeviceToken);
}
if (deviceTokens.Count > 0)
{
using (dotAPNSService service = new dotAPNSService())
{
service.OnTokenExpiredHandler += new EventHandler(OnTokenExpired);
service.SendSilentNotifications(deviceTokens.ToArray());
}
}
}
}
finally
{
}
}
To remove the expired tokens from the database you can use the following:
private void OnTokenExpired(object sender, EventArgs e)
{
if (e == null)
return;
if (e.GetType() == typeof(ExpiredTokenEventArgs))
{
var args = (ExpiredTokenEventArgs)e;
User user = _accountService.GetUserByDeviceToken(args.Token);
if (user != null)
{
user.DeviceToken = String.Empty;
Boolean success = !(_accountService.SaveUser(user) == null);
if (success)
// INFO - expired device token has been removed from database
else
// INFO - something went wrong
}
}
}
You can download the source code from here:
https://github.com/alexalok/dotAPNS
The API is now sending thousands of silent notifications at one time and there are no delays, crashes etc. Hope this code snippet helps and saves your time!

Xamarin forms callkit integration

I am trying to develop a xamarin forms app in which user can make call
(Navigate to dialer) from taping on number showed on app.In android I accomplished this through dependency service.But in ios I am stuck.I heard about callkit.I saw the documentation of it in https://learn.microsoft.com/en-us/xamarin/ios/platform/callkit?tabs=windows. But how can I actually implement on this in my App? I added all the classes in that document to my app.But how I can make the call from xamal.cs to the ios specified code? By using Dependency service?
Edit: I know how to navigate app to dialer or phone app. Why I am using callkit is I want to get the call duartion.
I created an Instance
public interface IosCallerDialer
{
void StartCall();
}
Implementation on ios
class IosCallDial: IosCallerDialer
{
private CXCallController CallController = new CXCallController();
private void SendTransactionRequest(CXTransaction transaction)
{
// Send request to call controller
CallController.RequestTransaction(transaction, (error) => {
// Was there an error?
if (error == null)
{
// No, report success
Console.WriteLine("Transaction request sent successfully.");
}
else
{
// Yes, report error
Console.WriteLine("Error requesting transaction: {0}", error);
}
});
}
public void StartCall()
{
// Build call action
string contact = "8547085532";
var handle = new CXHandle(CXHandleType.Generic, contact);
var startCallAction = new CXStartCallAction(new NSUuid(), handle);
// Create transaction
var transaction = new CXTransaction(startCallAction);
// Inform system of call request
SendTransactionRequest(transaction);
}
}
My xaml.cs
async void btnCall_Clicked(object sender, System.EventArgs e)
{
DependencyService.Get<IosCallerDialer>().StartCall();
}
Apart this I added all the classes defined in the document.I want only outgoing call. Is this proper way? I cant find any tutorials regarding callkit on xamarin. Any help is appreciated.
EDIT: I understand Callkit only for voip. So is there any other workaround like starting a timer when moves to phone app and stop timer when returns to app? Is it possible? Please provide any insights.
You can try the code below to detect the state of incoming call.
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public CTCallCenter c { get; set; }
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
c = new CTCallCenter();
c.CallEventHandler = delegate (CTCall call)
{
if (call.CallState == call.StateIncoming)
{
//start the timer
}
else if (call.CallState == call.StateDialing)
{
}
else if (call.CallState == call.StateConnected)
{
}
else if(call.CallState == call.StateDisconnected)
{
//end the timer
//use messagecenter to send duartion
MessagingCenter.Send<Object>(new Object(), "Hi");
}
};
return base.FinishedLaunching(app, options);
}
}
And any Where in Xamarin.forms:
MessagingCenter.Subscribe<Object>(this, "Hi", (sender) => {
// do something whenever the "Hi" message is sent
Console.WriteLine("hihihi");
});
Note: I haven't test it on my side yet as I don't have enough device. You can test it and let me know if it works.

Task Cancel exception for an api call in Xamain.Forms for iOS real device only

I am doing an app in which i am calling a web api. I am getting task cancel exception when i call the api in iPad/iPhone.But the response is fine in simulator and android devices.Can anyone please help me in resolving the issue.I am not able to fine where it is going wrong.
Thanks in advance.
Try using Native Http Handler to see it improves.
Declare an interface.
public interface IHttpHandler
{
HttpClient ReturnHandler();
}
Implement it this way.
public class HttpHandler : IHttpHandler
{
public HttpClient ReturnHandler()
{
try
{
var client = new HttpClient(new NSUrlSessionHandler()
{
});
client.Timeout = TimeSpan.FromSeconds(120);
return client;
}
catch (TaskCanceledException ex)
{
Console.WriteLine("TaskCanceledException ReturnHandler-->" + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("TaskCanceledException ReturnHandler-->" + ex.InnerException.Message);
}
return null;
}
catch (Exception ex)
{
Console.WriteLine("ReturnHandler Exception-->" + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("ReturnHandler Exception-->" + ex.InnerException.Message);
}
return null;
}
}
}
Use Xamarin forms inbuilt Dependency injection to utilise the interface I gave you to have HTTPClient with native handler.
If you don't know how to read Here

Pushsharp 4.0 with Firebase

After reading lot of post I could not find a complete example how to send GCM push notifications using Pushsharp 4.0 with Firebase. Lot of examples with PushSharp are using the old Google cloud messaging, not Firebase and/or the old PushSharp version.
Does anyone have an example of a stable and working code for sending GCM push notifications using PushSharp 4.0 with Firebase ?
You can try this: https://github.com/Redth/PushSharp/issues/711
I haven't tried for myself but from the comments in above post, it appears people had success with PushSharp and Firebase with the suggested change.
I was able to get PushSharp to work with FCM relatively easily. I thought it would be much harder. Here are the steps I took:
I created a new project on console.firebase.google.com
Then I went to settings => Cloud Messaging. I used that SenderID as my PushSharp sender ID and Legacy Server Key as my PushSharp Authentication Key.
Next, I went to the google play console, selected the project, then Development Tools => Services and API.
Then I linked the App project to the new FCM project by entering the SenderID in the "Linked Sender ID" field.
That should be all you do for the server to be working.
Now, I needed to hook up the App to this new SenderID. I think this is usually done by entering the google-services.json file as shown in the Firebase setup. However, I use PhoneGap/Cordova plug in for sending notifications. So, I do this:
Change the GCM_SENDER_ID to the SenderID above.
Unfortunately, I had to redistribute the new version of the app to get it to work. But it did work.
Hope this helps someone else.
This console program worked fine for me.
class Program
{
static void Main(string[] args)
{
try
{
string token = "putYourSecretTokenHere";
using (var s = new FcmPushNotificationService())
{
s.SendPushNotification(token);
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
public sealed class FcmPushNotificationService : IDisposable
{
#region Constructors
public FcmPushNotificationService()
{
string serverKey = "Legacy server key";
this.Config = new GcmConfiguration(serverKey);
this.Config.GcmUrl = "https://fcm.googleapis.com/fcm/send";
this.Broker = this.InitializeBroker();
}
#endregion
#region Properties
private GcmServiceBroker Broker { get; }
private GcmConfiguration Config { get; }
#endregion
#region Private Methods
private GcmServiceBroker InitializeBroker()
{
var gcmServiceBroker = new GcmServiceBroker(this.Config);
gcmServiceBroker.OnNotificationSucceeded += this.OnNotificationSucceeded;
gcmServiceBroker.OnNotificationFailed += this.OnNotificationFailed;
gcmServiceBroker.Start();
return gcmServiceBroker;
}
#endregion
#region Event Handlers
private void OnNotificationFailed(GcmNotification gcmNotification, AggregateException aggregateEx)
{
aggregateEx.Handle(ex =>
{
// See what kind of exception it was to further diagnose
if (ex is GcmNotificationException notificationException)
{
Console.WriteLine($"Notification of {string.Join(", ", notificationException.Notification.RegistrationIds)} failed: {notificationException.Message}");
}
else if (ex is GcmMulticastResultException multicastException)
{
Console.WriteLine($"Notification of {string.Join(", ", multicastException.Succeeded.SelectMany(n => n.RegistrationIds))} succeeded.");
Console.WriteLine($"Notification of {string.Join(", ", multicastException.Failed.SelectMany(n => n.Key.RegistrationIds))} failed: {multicastException.Message}");
}
else if (ex is DeviceSubscriptionExpiredException expiredException)
{
Console.WriteLine($"Device registration id expired: {expiredException.OldSubscriptionId}. Device registration id changed to {expiredException.NewSubscriptionId}");
}
else if (ex is RetryAfterException retryException)
{
Console.WriteLine($"FCM rate limited, don't send more until after {retryException.RetryAfterUtc}");
}
else
{
Console.WriteLine($"Failed to send notification {ex}");
}
// Mark it as handled
return true;
});
}
private void OnNotificationSucceeded(GcmNotification gcmNotification)
{
Console.WriteLine($"Notification sent to {string.Join(", ", gcmNotification.RegistrationIds)}. Data: {gcmNotification.Data}, Notification: {gcmNotification.Notification}");
}
#endregion
#region IDisposable Members
/// <inheritdoc cref="IDisposable"/>
public void Dispose()
{
this.Broker?.Stop();
}
#endregion
#region IPushNotificationService Members
///<inheritdoc/>
public void SendPushNotification(string token)
{
var notification = JObject.Parse("{\"title\": \"Test\",\"body\": \"Success!\"}");
this.Broker.QueueNotification(new GcmNotification
{
RegistrationIds = new List<string> { token },
Notification = notification
});
}
#endregion
}
}

Sharing SignalR hub with ASP.NET Website and a Web API Mobile Backend

I've set up SignalR Realtime communication for my ASP.NET based website. However, I need to know if it's possible to share the same SignalR Hub with a separate Mobile Backend Project which deals with the same server and database. Basically like accessing the facebook's inbox through web and mobile at the same time?
That would be a ASP.NET Web project and a Mobile Client connected to one hub.
If you want to find SignalR for Android, I suggest the following working link for your to start
SignalR/java-client at GitHub
You can refer to the following sample code (this is from my question on SO about SignalR for Android: SignalR for Android: how can I pass dynamic class to SubscriptionHandler1
public <T> void startSignalR(String transport, String serverUrl, final String userName, final Class<T> tClass) {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader(HEADER_KEY_USERNAME, userName);
}
};
mConnection = new HubConnection(serverUrl);
mConnection.setCredentials(credentials);
mHub = mConnection.createHubProxy(SERVER_HUB_CHAT);
if (transport.equals("ServerSentEvents")) {
mTransport = new ServerSentEventsTransport(mConnection.getLogger());
} else if (transport.equals("LongPolling")) {
mTransport = new LongPollingTransport(mConnection.getLogger());
}
mAwaitConnection = mConnection.start(mTransport);
try {
mAwaitConnection.get();
} catch (InterruptedException e) {
e.printStackTrace();
return;
} catch (ExecutionException e) {
e.printStackTrace();
return;
}
mHub.on("broadcastMessage",
new SubscriptionHandler1<Object>() {
#Override
public void run(final Object msg) {
final String finalMsg;
Gson gson = new Gson();
Object object = gson.fromJson(msg.toString(), tClass);
Field[] fields = object.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
try {
System.out.println("Value = " + fields[i].get(object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
, Object.class);
...
}
Hope this helps!

Resources