Azure Notification Hub and WP8 Intermitant notifications - windows-phone-7

This is a fairly long piece of code but I am getting nowhere with this and cannot see any issues, although I am new to using notification hubs. I am trying to register for targeted notifications (the logged on user) using the notification hub in Azure. After the registration, a test notification is sent.
The issue I am having is that sometimes the notification is sent to the device, and sometimes it is not. It mostly isn't but occasionally when I step through the code on the server, i will get the notification on the emulator come through. Once when I deployed the app to my phone the notification came though on the emulator! I cannot discover a pattern.
My Controller class looks like this;
private NotificationHelper hub;
public RegisterController()
{
hub = NotificationHelper.Instance;
}
public async Task<RegistrationDescription> Post([FromBody]JObject registrationCall)
{
var obj = await hub.Post(registrationCall);
return obj;
}
And the helper class (which is used elsewhere so is not directly in the controller) looks like this;
public static NotificationHelper Instance = new NotificationHelper();
public NotificationHubClient Hub { get; set; }
// Create the client in the constructor.
public NotificationHelper()
{
var cn = "<my-cn>";
Hub = NotificationHubClient.CreateClientFromConnectionString(cn, "<my-hub>");
}
public async Task<RegistrationDescription> Post([FromBody] JObject registrationCall)
{
// Get the registration info that we need from the request.
var platform = registrationCall["platform"].ToString();
var installationId = registrationCall["instId"].ToString();
var channelUri = registrationCall["channelUri"] != null
? registrationCall["channelUri"].ToString()
: null;
var deviceToken = registrationCall["deviceToken"] != null
? registrationCall["deviceToken"].ToString()
: null;
var userName = HttpContext.Current.User.Identity.Name;
// Get registrations for the current installation ID.
var regsForInstId = await Hub.GetRegistrationsByTagAsync(installationId, 100);
var updated = false;
var firstRegistration = true;
RegistrationDescription registration = null;
// Check for existing registrations.
foreach (var registrationDescription in regsForInstId)
{
if (firstRegistration)
{
// Update the tags.
registrationDescription.Tags = new HashSet<string>() {installationId, userName};
// We need to handle each platform separately.
switch (platform)
{
case "windows":
var winReg = registrationDescription as MpnsRegistrationDescription;
winReg.ChannelUri = new Uri(channelUri);
registration = await Hub.UpdateRegistrationAsync(winReg);
break;
case "ios":
var iosReg = registrationDescription as AppleRegistrationDescription;
iosReg.DeviceToken = deviceToken;
registration = await Hub.UpdateRegistrationAsync(iosReg);
break;
}
updated = true;
firstRegistration = false;
}
else
{
// We shouldn't have any extra registrations; delete if we do.
await Hub.DeleteRegistrationAsync(registrationDescription);
}
}
// Create a new registration.
if (!updated)
{
switch (platform)
{
case "windows":
registration = await Hub.CreateMpnsNativeRegistrationAsync(channelUri,
new string[] {installationId, userName});
break;
case "ios":
registration = await Hub.CreateAppleNativeRegistrationAsync(deviceToken,
new string[] {installationId, userName});
break;
}
}
// Send out a test notification.
await SendNotification(string.Format("Test notification for {0}", userName), userName);
return registration;
And finally, my SendNotification method is here;
internal async Task SendNotification(string notificationText, string tag)
{
try
{
var toast = PrepareToastPayload("<my-hub>", notificationText);
// Send a notification to the logged-in user on both platforms.
await NotificationHelper.Instance.Hub.SendMpnsNativeNotificationAsync(toast, tag);
//await hubClient.SendAppleNativeNotificationAsync(alert, tag);
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
Console.WriteLine(ex.Message);
}
}
I suspect the issue is in my phone client code, which is here and SubscribeToService is called immediately after WebAPI login;
public void SubscribeToService()
{
_channel = HttpNotificationChannel.Find("mychannel");
if (_channel == null)
{
_channel = new HttpNotificationChannel("mychannel");
_channel.Open();
_channel.BindToShellToast();
}
_channel.ChannelUriUpdated += async (o, args) =>
{
var hub = new NotificationHub("<my-hub>", "<my-cn>");
await hub.RegisterNativeAsync(args.ChannelUri.ToString());
await RegisterForMessageNotificationsAsync();
};
}
public async Task RegisterForMessageNotificationsAsync()
{
using (var client = GetNewHttpClient(true))
{
// Get the info that we need to request registration.
var installationId = LocalStorageManager.GetInstallationId(); // a new Guid
var registration = new Dictionary<string, string>()
{
{"platform", "windows"},
{"instId", installationId},
{"channelUri", _channel.ChannelUri.ToString()}
};
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(ApiUrl + "api/Register/RegisterForNotifications"));
request.Content = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json");
string message;
try
{
HttpResponseMessage response = await client.SendAsync(request);
message = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
message = ex.Message;
}
_registrationId = message;
}
}
Any help would be greatly appriciated as I have been stuck on this now for days! I know this is a lot of code to paste up here but it is all relevant.
Thanks,
EDIT: The SubscribeToService() method is called when the user logs in and authenticates with the WebAPI. The method is here;
public async Task<User> SendSubmitLogonAsync(LogonObject lo)
{
_logonObject = lo;
using (var client = GetNewHttpClient(false))
{
var logonString = String.Format("grant_type=password&username={0}&password={1}", lo.username, lo.password);
var sc = new StringContent(logonString, Encoding.UTF8);
var response = await client.PostAsync("Token", sc);
if (response.IsSuccessStatusCode)
{
_logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
var userInfo = await GetUserInfoAsync();
if (_channel == null)
SubscribeToService();
else
await RegisterForMessageNotificationsAsync();
return userInfo;
}
// ...
}
}

I have solved the issue. There are tons of fairly poorly organised howto's for azure notification hubs and only one of them has this note toward the bottom;
NOTE:
You will not receive the notification when you are still in the app.
To receive a toast notification while the app is active, you must
handle the ShellToastNotificationReceived event.
This is why I was experiencing intermittent results, as i assumed you would still get a notification if you were in the app. And this little note is pretty well hidden.

Have you used proper tag / tag expressions while register/send the message. Also, Where are you storing the id back from the notification hub. It should be used when you update the channel uri (it will expire).
I would suggest to start from scratch.
Ref: http://msdn.microsoft.com/en-us/library/dn530749.aspx

Related

Unable to Run AudioGraph in Background Task

I'm developing a Xamarin application. It's a glorified audio player. I created a background task for the UWP part which extends IBackgroundTask from Windows.ApplicationModel.Background.
using Windows.ApplicationModel.Background;
public sealed class BackgroundServiceManager : IBackgroundTask {
private IIpcNode m_node;//Sends/Receive message powered by LocalSettings
private BackgroundTaskDeferral m_deferral;
public void Run(IBackgroundTaskInstance taskInstance) {
DebugUtils.Log("BackgroundServiceManager.Run()");
m_deferral = taskInstance.GetDeferral();
m_services = new List<IBackgroundService> {
(m_playbackService = new PlaybackService()),
(m_smartLightService = new SmartLightsService())
};
this.RegisterServices();
taskInstance.Task.Completed += OnTaskCompleted;
taskInstance.Canceled += OnTaskCanceled;
//Listen to messages powered by LocalSettings
m_node.StartListening();
}
//...
}
I start the task using an application trigger. The background task lives in a separate project.
public async Task InitializeBackgroundTask() {
var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
localSettings.Values.Clear();
BackgroundExecutionManager.RemoveAccess();
var accessStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (accessStatus == BackgroundAccessStatus.DeniedBySystemPolicy
|| accessStatus == BackgroundAccessStatus.DeniedByUser
|| accessStatus == BackgroundAccessStatus.Unspecified)
return;
UnregisterExistingTasks();
var builder = new BackgroundTaskBuilder() {
Name = BACKGROUND_TASK_NAME,
TaskEntryPoint = BACKGROUND_TASK_ENTRY_POINT,
};
var applicationTrigger = new ApplicationTrigger();
builder.SetTrigger(applicationTrigger);
var registration = (BackgroundTaskRegistration)null;
try {
registration = builder.Register();
}
catch (Exception ex) {
throw new Exception(string.Format("Unable to register {0} background task.", BACKGROUND_TASK_NAME), ex);
}
registration.Completed += OnBackgroundTaskCompleted;
registration.Progress += OnRegistrationProgress;
this.Node.StartListening();
var result = await applicationTrigger.RequestAsync();
if (result != ApplicationTriggerResult.Allowed)
throw new Exception("Unable to trigger background task.");
}
I have a background task declaration in my main UWP project. The task type is "general" and I provide the proper entry point.
Also, the Background Media Playback capability is checked.
The task works perfectly fine, but AudioGraph doesn't generate sounds on the background task. MediaPlayer does, however. I need AudioGraph as it allows for seamless audio looping.
Here's my AudioGraph test method. I created it to isolate AudioGraph-related code:
public static async Task TestAudioGraph(int toneFrequency) {
var audioFileInfo = await AudioUtils.CreateToneFile(toneFrequency);
var settings = new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Media);
var createAudioGraphResult = await AudioGraph.CreateAsync(settings);
if (createAudioGraphResult.Status != AudioGraphCreationStatus.Success)
throw new Exception(createAudioGraphResult.Status.ToString());
var audioGraph = createAudioGraphResult.Graph;
var createDeviceOutputNodeResult = await audioGraph.CreateDeviceOutputNodeAsync();
if (createDeviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
throw new Exception(createDeviceOutputNodeResult.Status.ToString());
var deviceOutputNode = createDeviceOutputNodeResult.DeviceOutputNode;
var storageFile = await StorageFile.GetFileFromPathAsync(audioFileInfo.FileFullPath);
var createFileInputNodeResult = await audioGraph.CreateFileInputNodeAsync(storageFile);
if (createFileInputNodeResult.Status != AudioFileNodeCreationStatus.Success)
throw new Exception(createFileInputNodeResult.Status.ToString());
var fileInputNode = createFileInputNodeResult.FileInputNode;
fileInputNode.OutgoingGain = 1;
fileInputNode.LoopCount = Int32.MaxValue;
fileInputNode.AddOutgoingConnection(deviceOutputNode);
fileInputNode.Start();
audioGraph.Start();
await Task.Delay(3000);
}
Sending of sound test message from foreground:
public async Task TestSound() {
//Test sound on foreground first
await Task.Run(async () => await UWPUtils.TestAudioGraph(200));
var message = new UWPIpcMessage("TESTSOUND", null);
await this.Node.SendMessage(message, true);
}
Background's handling of sound test message:
m_node.Subscribe("TESTSOUND", async (message) => {
//Then test sound on background
await Task.Run(async () => await UWPUtils.TestAudioGraph(800));
return null;
});
Would you happen to know why AudioGraph doesn't work? Am I missing a setting or is this possible at all?
I look on the web and couldn't find examples or working samples, just hints that what I'm attempting is possible.
P-S: My task type is "general", because I found no way of triggering "audio" type of tasks.
Thank you

How to do sign out from MSAL authentication.?

I am working on xamarin forms where I am using MSAL for authentication to authenticate. Now I want to sign out the user once he clicks on the sign-out. For sign out, I wrote Below code
foreach (var user in await App.PCA.GetAccountsAsync())
{
await App.PCA.RemoveAsync(user);
}
The above code is executing without any problem but when I again try to log in it is not asking for the password. I am clearing the cookies from the app using dependency service like
created one interface in xamarin forms project
public interface IAuthentication
{
void ClearAllCookies();
}
And in the android project providing the implementation for the interface
public void ClearAllCookies()
{
CookieManager.Instance.RemoveSessionCookie();
CookieManager.Instance.RemoveAllCookie();
}
I am doing Authentication like below
In App.xamal.cs file
public static IPublicClientApplication PCA = null;
public static string ClientID = "xxxxxxxxxxxxxxxxxxxx";
public static string[] Scopes = { "User.Read" };
public App()
{
PCA = PublicClientApplicationBuilder.Create(ClientID)
.WithRedirectUri($"msal{ClientID}://auth")
.Build();
InitializeComponent();
}
//On login button click
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithLoginHint(EmailId) //Here I am passing Email Id
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
}
catch (Exception ex2)
{
await DisplayAlert("Acquire token interactive failed. See exception message for details: ", ex2.Message, "Dismiss");
}
}
if (authResult != null)
{
var content = await GetHttpContentWithTokenAsync(authResult.AccessToken);
await Navigation.PushModalAsync(new Dashboard.PartnerDashboard(content));
}
}
After clearing again it is not asking for the password. If I uninstall and reinstall the app also it is not asking for the password. How to resolve this?
Can you try with the below code for clearing accounts in MSAL.
public void ClearAllCookies(string authority)
{
var authContext = new AuthenticationContext(authority);
authContext.TokenCache.Clear();
CookieManager.Instance.RemoveSessionCookie();
CookieManager.Instance.RemoveAllCookie();
}
where authority = "https://login.windows.net/common";
Try below code before authenticate code running, for removing old accounts.
var authContext = new AuthenticationContext(authority);
if(authContext.TokenCache.ReadItems().Any())
{
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority,false, null);
}

Httpclient Slow Performance same computer Android Emulator Xamarin

I am using the HttpClient but my results are taking up to 6 seconds coming back from the same machine on the same subnet and ip range of 192.168. When I call the api directly from the ip address the results are more or less instant so why is it so slow with httpclient on the same computer.
I have seen other so's that suggest set to use proxy as false is the best way to go.
I have also tested this on a stock phone and it takes around 8 seconds for the login to be successful on the phone.
private HttpClient _client;
public async Task<String> Getusers()
{
var content = "";
HttpClientHandler hch = new HttpClientHandler();
hch.Proxy = null;
hch.UseProxy = false;
_client = new HttpClient(hch);
var uri = new Uri(Constants.ApiEndPoint + "/Users"); // Your url is here
try
{
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
throw ex;
}
return content;
}
Here is my login method in case anyone can see something wrong with it.
private async void BtnLogin_Clicked(object sender, EventArgs e)
{
string content = await Getusers(); //Sends a GET request to the specified Uri and returns the response body as a string in an asynchronous operation
List<Users> _users = JsonConvert.DeserializeObject<List<Users>>(content); //Deserializes or converts JSON String into a collection of Post
var userName = txtUserName.Text;
var password = txtPassword.Text;
var isValidUser = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault();
var driverId = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault().ID;
if (isValidUser != null)
{
Application.Current.Properties["driverId"] = driverId;
Application.Current.MainPage = new MainPage();
}
else
{
lblError.Text = "Error your credentials are invalid, please try again";
}
}

How to use Xamarin.Auth in Xamarin.Forms (Shared Project for iOS and Android)?

I am trying to use Xamarin.Auth for a Facebook signin. It's all set up, but I am missing the last part. The Facebook signin is not my MainActivity, one has to click on a button in order to sign in. I don't know how to start the page for the signin. I have trying to follow this approach, but as my MainActivity isn't the Facebook signin page, it won't work. I have binded (I am following the MVVM pattern) the signin button and have implemented an interface in order to use DependencyService. My problem is when I have to implement the code of the platforms.
This is what I have tried on Android:
class LoginFBImpl : Activity, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB() //the method in the interface
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var intent = authenticator.GetUI(this);
StartActivity(intent); //Problem occurs here
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationCancelled()
{
}
public void OnAuthenticationFailed(string message, Exception exception)
{
}
}
iOS:
public class LoginFBImpl : UIViewController, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB()
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var viewController = authenticator.GetUI();
PresentViewController(viewController, true, null);
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
DismissViewController(true, null);
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationFailed(string message, Exception exception)
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = message,
Message = exception?.ToString()
};
PresentViewController(alertController, true, null);
}
public void OnAuthenticationCancelled()
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = "Authentication cancelled",
Message = "You didn't complete the authentication process"
};
PresentViewController(alertController, true, null);
}
}
I think it has something to do with the Activity/ViewController, but I don't know how to do it properly. When I run this I get: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference on Android, and I am expecting something similar on iOS - Haven't tested it yet as I am working on Windows.

How to make async call to api (http). in c#. inside a Task

I am developeing a chatbot using microsoftbotframmwok where I have some requirement to make a call from my task to an api(httpclient). but it is not working. when i test the api from an stand alone console application in side main method it works. but in my application it doesn't work.
I tried to call an api from an simple method without task but when it makes a cal its basically halts or stucked somewhere, i converted my function into task and while making an api call i used await keyword to call it asynchronously but it is returning error, while reading it not the result.
here is the my code which make an api call
private async Task<String> getProblem(IDialogContext context)
{
var response = "Thannks for contacting..";
//here some code logix..
SnowApiClient client = new SnowApiClient(Url, UserId, ApiPassword);
IncidentRequestPayload payload = new IncidentRequestPayload();
payload.caller_id = "tet111";
payload.assignment_group = "it";
payload.category = "complaint";
payload.u_feedback_type = "Praise";
payload.service_offering = "Application Management";
payload.priority = 2;
payload.short_description = "computer battery is dead";
payload.comments = String.Empty;
ApiResponse objResponse = await client.CreateIncident(payload);
//objResponse.payload.number;
return response;
}
//code for CreateIncident...in Api project librarary
public async Task<ApiResponse> CreateIncident(IncidentRequestPayload payload)
{
var incidentRequest = new ApiRequest { method = CreateIncidentMethod, payload = payload };
var createResult = await ExecuteRequest(incidentRequest);
return await ReadIncident(createResult.payload.incNumber);
}
public async Task<ApiResponse> ReadIncident(string number)
{
var incidentRequest = new ApiRequest { method = ReadIncidentMethod, payload = new RequestPayload { number = number } };
return await ExecuteRequest(incidentRequest);
}
private async Task<ApiResponse> ExecuteRequest(ApiRequest requestObject)
{
HttpResponseMessage response = await _client.PostAsJsonAsync("/SRintegratedAPI.rest", requestObject);
ApiResponse responseObject = null;
if (response.IsSuccessStatusCode)
{
responseObject = await response.Content.ReadAsAsync<ApiResponse>();
}
else
{
throw new System.Net.WebException(response.ReasonPhrase);
}
if (responseObject.result != "ok")
{
throw new System.Net.WebException(responseObject.message);
}
return responseObject;
}
I don't understand how and where do i used async/await here in basicalaly in my getProblem function.
please help

Resources