Xamarin Push Notification - xamarin

Receiving and opening notifications with the app in the background increments the number of Data sent.
In addition the notification is not showing when the app is killed or when it is in the foreground.
Below is my code, the source code is from Gerald Versluis https://github.com/jfversluis/XFFCMPushNotificationsSample
using System;
using Xamarin.Forms;
using Plugin.FirebasePushNotification;
using Xamarin.Forms.Xaml;
namespace FCMTest
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
CrossFirebasePushNotification.Current.Subscribe("all");
CrossFirebasePushNotification.Current.OnTokenRefresh += Current_OnTokenRefresh;
CrossFirebasePushNotification.Current.OnNotificationReceived += Current_OnNotificationReceived;
CrossFirebasePushNotification.Current.OnNotificationOpened += Current_OnNotificationOpened;
//CrossFirebasePushNotification.Current.OnNotificationAction += Current_OnNotificationAction;
}
private void Current_OnTokenRefresh(object source, FirebasePushNotificationTokenEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"Token from function OnTokenRefresh: {e.Token}");
}
private void Current_OnNotificationReceived(object source, FirebasePushNotificationDataEventArgs d)
{
System.Diagnostics.Debug.WriteLine("Received");
foreach (var data in d.Data)
{
System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
}
}
/*private void Current_OnNotificationAction(object source, FirebasePushNotificationResponseEventArgs d)
{
System.Diagnostics.Debug.WriteLine("Opened");
foreach (var data in d.Data)
{
System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
}
}*/
private void Current_OnNotificationOpened(object source, FirebasePushNotificationResponseEventArgs d)
{
System.Diagnostics.Debug.WriteLine("Opened");
foreach (var data in d.Data)
{
System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
}
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
using System;
using System.Collections.Generic;
namespace FCMDispatcher
{
class Program
{
static void Main(string[] args)
{
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("fcm-test-98fe5-firebase-adminsdk-oknwc-c290af4ff0.json")
});
// This registration token comes from the client FCM SDKs.
var registrationToken = "d8yECxIhJXQ:APA91bFGXSrnUVcP07TlN4HpvfstWwEdPQaj4wr6Z3Q-7JqcJJjrFy9LkWdlfzcCNDrZeVy55IDTWxvp5Gfyv8318uRRmIPo6Gp2IQnyUDSHqGLdTF8RMmlwyaECTKWDnmhClMLV8In9";
// See documentation on defining a message payload.
var message = new Message()
{
Data = new Dictionary<string, string>()
{
{"myData", "One more try to say Succeded!"},
},
Token = registrationToken,
//Topic = "all",
Notification = new Notification()
{
Title = "Test from code",
Body = "Here is your test!"
}
};
// Send a message to the device corresponding to the provided
// registration token.
string response = FirebaseMessaging.DefaultInstance.SendAsync(message).Result;
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
}
}
}

Try adding priority with high or max to your Data Payload.
Data = new Dictionary<string, string>()
{
{"myData", "One more try to say Succeded!"},
{"priority", "high"},
},
This complex condition must match to show your notification:
https://github.com/CrossGeeks/FirebasePushNotificationPlugin/blob/d86266a9f45687b418f5f1e69c348681d1ff6e27/Plugin.FirebasePushNotification/DefaultPushNotificationHandler.android.cs#L151

Related

Xamarin Forms MessagingCenter Subscribe called two times

I'm clicking on a product item in listview in product page viewmodel to show a popup(using rg.plugin popup) for selecting one of the product variants.After selecting variant,i am sending the selected variant to product page using messagingcenter from variant popup page viewmodel,subscribed in product page viewmodel constructor. working fine there.when i navigate to the previous page and then came back to this product page for adding one or more variant to the
same previously selected product,Messagingcenter subscribe called twice and product value increased twice.Tried to subscribe in the product page onappearing and unsubscribe in disappearing method.still calling two times? How to solve this issue?
calling popup:
var result = await dataService.Get_product_variant(store_id, product_id);
if (result.status == "success")
{
ind_vis = false;
OnPropertyChanged("ind_vis");
App.Current.Properties["product_variant_result"] = result;
App.Current.Properties["cartitems"] = purchaselist;
App.Current.Properties["selected_product"] = product_List2 ;
await PopupNavigation.Instance.PushAsync(new Popup_variant());
}
popup viewmodel: sending message
public Popup_variant_vm()
{
Radio_btn = new Command<Product_variant_list2>(Radio_stk_tapped);
product_variant_list = new List<Product_variant_list2>();
purchaselist = new ObservableCollection<Product_list2>();
show_variants();
}
internal void Confirm_variant()
{
if(App.Current.Properties.ContainsKey("selected_variant"))
{
MessagingCenter.Send<Popup_variant_vm, object>(this, "selected_variant", App.Current.Properties["selected_variant"]); //Message send from popup to product page
}
else
{
DependencyService.Get<IToast>().LongAlert("Please select any size");
}
}
product page viewmodel: subscribed here..called twice when navigating from previous page to this
public Store_page()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
var vm = new store_page_vm();
vm.Navigation = Navigation;
BindingContext = vm;
MessagingCenter.Unsubscribe<Popup_variant_vm, object>(this, "selected_variant");
MessagingCenter.Subscribe<Popup_variant_vm, object>(this, "selected_variant",async (sender, selected_variant) =>
{
var vm1 = BindingContext as store_page_vm;
vm1?.Addcart2(selected_variant);// called twice
});
}
unsubscribed in product cs page
protected override void OnDisappearing()
{
var vm = BindingContext as store_page_vm;
vm?.Save_cart();
MessagingCenter.Unsubscribe<Popup_variant_vm>(this, "selected_variant");
}
Your unsubscription should look something like below and it should work :
MessagingCenter.Unsubscribe<Popup_variant_vm, object>(this, "selected_variant");
https://stackoverflow.com/a/44753021/10937160
try this, and make sure you do not call Subscribe more than once.
My solution:
put unsubscribe sentence into subscribe body !!
MessagingCenter.Subscribe<object, string>(this, "IdSearch", (sender, arg) =>
{
listView.ItemsSource = arg;
MessagingCenter.Unsubscribe<object, string>(this, "IdSearch");
}, BindingContext);
I have created static counter variable in my app the in subscriber I have done this:
public static class Constants
{
public static int msgCenterSubscribeCounter { get; set; } = 0;
}
MessagingCenter.Subscribe<object, string>(this, "hello", (sender, arg) =>
{
Constants.msgCenterSubscribeCounter++;
if (arg.Equals("hello") && Constants.msgCenterSubscribeCounter == 1)
{
// handle your logic here
}
});
Reset counter in OnDisappearing() method from where you have called Send.
Changing Messagingcenter in to single subscription.
public class Messagingcenter_singleton
{
private static Messagingcenter_singleton _instance;
private bool isActivated = false;
private Action<string> callBackFun = null;
public static Messagingcenter_singleton Instance()
{
if (_instance == null)
{
_instance = new Messagingcenter_singleton();
}
return _instance;
}
public void setCallBack(Action<string> eventCallBack)
{
callBackFun = eventCallBack;
}
public void startSubscribe()
{
if (!isActivated)
{
isActivated = true;
MessagingCenter.Subscribe<string, string>(this, "Name", eventCallBack);
}
}
private void eventCallBack(string arg1, string arg2)
{
if (callBackFun != null)
{
InvokeMethod(new Action<string>(callBackFun), arg2);
}
}
public static object InvokeMethod(Delegate method, params object[] args)
{
return method.DynamicInvoke(args);
}
}
Use Below Code in you view model class
public void initSubscribe()
{
Messagingcenter_singleton.Instance().startSubscribe();
Messagingcenter_singleton.Instance().setCallBack(eventCallBack)
}
public void eventCallBack(string arg2)
{
// write your code here
}

Using UIDocumentPickerViewController in Xamarin forms as a dependency service

I'm using Xamarin forms and writing a dependency service for the following objectives :
Open iOS files app. (UIDocumentPickerViewController )
Select any kind of a document.
Copy that document into my application Documents directory. (For app access)
Show that document into my application by storing its path into my SQLite DB.
What I am trying to do here is call the Files app from my application on an Entry click and the click event seems to be working well my dependency service calls perfectly but now when I try to use the UIDocumentPickerViewController I am unable to get View controller context in my dependency service to call the PresentViewController method. Now I know about the xamarin forms context but I don't know if it will work here and I don't even know if it would be a smart idea to use it as it has already been marked as obsolete and since I am not from the iOS background, I don't know what would be the right solution for it.
My code is as follows :
public class DocumentPickerRenderer : IDocumentPicker
{
public object PickFile()
{
var docPicker = new UIDocumentPickerViewController(new string[] { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += (sender, wasCancelledArgs) =>
{
};
docPicker.DidPickDocumentAtUrls += (object sender, UIDocumentPickedAtUrlsEventArgs e) =>
{
Console.WriteLine("url = {0}", e.Urls[0].AbsoluteString);
//bool success = await MoveFileToApp(didPickDocArgs.Url);
var success = true;
string filename = e.Urls[0].LastPathComponent;
string msg = success ? string.Format("Successfully imported file '{0}'", filename) : string.Format("Failed to import file '{0}'", filename);
var alertController = UIAlertController.Create("import", msg, UIAlertControllerStyle.Alert);
var okButton = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>
{
alertController.DismissViewController(true, null);
});
alertController.AddAction(okButton);
PresentViewController(alertController, true, null);
};
PresentViewController(docPicker, true, null);
}
}
My questions:
Is my methodology correct for picking files?
what will be the object that I will be getting as a callback from a file selection and how will I get the callback?
Is there any other way or something available for xamarin forms, some guide or something that allows me to pick documents from my native file systems and gives a brief on how to handle it in both ios and android?
Hello Guys, You can use following code for picking any type of documents to mention in code using iOS Devices-
use follwing interface:
public interface IMedia
{
Task<string> OpenDocument();
}
public Task<string> OpenDocument()
{
var task = new TaskCompletionSource<string>();
try
{
OpenDoc(GetController(), (obj) =>
{
if (obj == null)
{
task.SetResult(null);
return;
}
var aa = obj.AbsoluteUrl;
task.SetResult(aa.Path);
});
}
catch (Exception ex)
{
task.SetException(ex);
}
return task.Task;
}
static Action<NSUrl> _callbackDoc;
public static void OpenDoc(UIViewController parent, Action<NSUrl> callback)
{
_callbackDoc = callback;
var version = UIDevice.CurrentDevice.SystemVersion;
int verNum = 0;
Int32.TryParse(version.Substring(0, 2), out verNum);
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image,
UTType.Spreadsheet,
"com.microsoft.word.doc",
"org.openxmlformats.wordprocessingml.document",
"com.microsoft.powerpoint.ppt",
"org.openxmlformats.spreadsheetml.sheet",
"org.openxmlformats.presentationml.presentation",
"com.microsoft.excel.xls",
};
// Display the picker
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Import);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
if (verNum < 11)
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Url);
pArgs.Url.StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Url.AbsoluteUrl);
};
}
else
{
args.DocumentPicker.DidPickDocumentAtUrls += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Urls[0]);
pArgs.Urls[0].StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Urls[0].AbsoluteUrl);
};
}
// Display the document picker
parent.PresentViewController(args.DocumentPicker, true, null);
};
pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
parent.PresentViewController(pickerMenu, true, null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover != null)
{
presentationPopover.SourceView = parent.View;
presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
}
}
Now you need to call using following code:
var filePath = await DependencyService.Get<IMedia>().OpenDocument();
For pick document in Android, you can use following code
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{ if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
For pick document in Android, you can use following code:
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
Use below code to access the helper class: public class Media:
IMedia {
public Task<string> OpenDocument() {
var task = new TaskCompletionSource<string>();
try {
IntentHelper.OpenDocPicker((path) => { task.SetResult(path); });
} catch (Exception ex) {
task.SetResult(null);
}
return task.Task;
}
}
Since I was looking for UIDocumentPickerViewController and not UIDocumentMenuViewController the other answer was not what I was looking for :
So this is how I ended up doing it:
Calling the document picker:
var docPicker = new UIDocumentPickerViewController(new string[]
{ UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += DocPicker_WasCancelled;
docPicker.DidPickDocumentAtUrls += DocPicker_DidPickDocumentAtUrls;
docPicker.DidPickDocument += DocPicker_DidPickDocument;
var _currentViewController = GetCurrentUIController();
if (_currentViewController != null)
_currentViewController.PresentViewController(docPicker, true, null);
Where GetCurrentUIController is the function to get the current UI controller something like this :
public UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
return null;
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
For below iOS 11 i added the DidPickDocument event:
private void DocPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
try
{
NSUrl filePath = e.Url.AbsoluteUrl;
//This is the url for your document and you can use it as you please.
}
catch (Exception ex)
{
}
}
For above iOS 11 you use the DidPickDocumentUrls since multipick is supported there :
private void DocPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
{
try
{
List<NSUrl> filePath = e.Urls.ToList().Select(y => y.AbsoluteUrl).ToList();
//returns the list of images selected
}
catch (Exception ex)
{
AppLogger.LogException(ex);
}
}

Xamarin Android using Azure Notification Hub not receiving notifications

We have developed a Android App using Xamarin and we are facing problem in Azure Notification Hub. We were trying to send push notifications to Android devices registered in our Azure Mobile Service and no registered android devices are receiving the notifications. When we tried to debug it via Notification Hub in Azure Portal, the results are showing that the notifications are sent to the devices. However, the devices are not receiving the notifications which was receiving before.
Kindly let us know either we are missing something in our code (Find the code below) or is there any problem in Azure Notification Hub (for Android GCM).
Note: All the android permissions for push notifications are given in the same code file below and not in the Android manifest.
Our GCM Service Code Below:
using System.Text;
using Android.App;
using Android.Content;
using Android.Util;
using Gcm.Client;
//VERY VERY VERY IMPORTANT NOTE!!!!
// Your package name MUST NOT start with an uppercase letter.
// Android does not allow permissions to start with an upper case letter
// If it does you will get a very cryptic error in logcat and it will not be obvious why you are crying!
// So please, for the love of all that is kind on this earth, use a LOWERCASE first letter in your Package Name!!!!
using ByteSmith.WindowsAzure.Messaging;
using System.Diagnostics;
using System.Collections.Generic;
using System;
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
//GET_ACCOUNTS is only needed for android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace seeMuscatAndroidApp
{
//You must subclass this!
[BroadcastReceiver(Permission= Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "#PACKAGE_NAME#" })]
public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
{
public static string[] SENDER_IDS = new string[] { Constants.SenderID };
public const string TAG = "GoogleCloudMessaging";
}
[Service] //Must use the service tag
public class GcmService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
Context _generalContext;
public GcmService() : base(PushHandlerBroadcastReceiver.SENDER_IDS)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "GcmService() constructor");
}
protected override async void OnRegistered (Context context, string registrationId)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Registered: " + registrationId);
RegistrationID = registrationId;
_generalContext = context;
//createNotification("GcmService Registered...", "The device has been Registered, Tap to View!");
Hub = new NotificationHub(Constants.NotificationHubPath, Constants.ConnectionString);
try
{
await Hub.UnregisterAllAsync(registrationId);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debugger.Break();
}
var tags = new List<string>() { main.userCountry, main.userCity, main.userLatitude, main.userLongitude, main.userPhoneMake, main.userPhoneModel, main.userPhoneName, main.userPhoneAndroidVersion, main.userAppVersion,main.userUID};
Console.WriteLine("///////////HUB TAGS///////////////////");
Console.WriteLine("Country:" + main.userCountry);
Console.WriteLine("City:" + main.userCity);
Console.WriteLine("Latitude:" + main.userLatitude);
Console.WriteLine("Longitude:"+main.userLongitude);
Console.WriteLine("Make:" + main.userPhoneMake);
Console.WriteLine("Model:" + main.userPhoneModel);
Console.WriteLine("Phone Name:" + main.userPhoneName);
Console.WriteLine("Android Version:" + main.userPhoneAndroidVersion);
Console.WriteLine("App version:" + main.userAppVersion);
Console.WriteLine("User ID:" + main.userUID);
Console.WriteLine("///////////END OF HUB TAGS///////////////////");
try
{
var hubRegistration = await Hub.RegisterNativeAsync(registrationId, tags);
Debug.WriteLine("RegistrationId:" + hubRegistration.RegistrationId);
}
catch (Exception ex)
{
Debug.WriteLine("#########$$$$Error:"+ex.Message);
}
}
protected override void OnUnRegistered (Context context, string registrationId)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Unregistered: " + registrationId);
//Remove from the web service
// var wc = new WebClient();
// var result = wc.UploadString("http://your.server.com/api/unregister/", "POST",
// "{ 'registrationId' : '" + lastRegistrationId + "' }");
//createNotification("GcmService Unregistered...", "The device has been unregistered, Tap to View!");
}
protected override void OnMessage (Context context, Intent intent)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "GCM Message Received!");
Debug.WriteLine("/********* GCM Received ****************");
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
//Store the message
var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
var edit = prefs.Edit();
edit.PutString("last_msg", msg.ToString());
edit.Commit();
string message = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(message))
{
createNotification("New todo item!", "Todo item: " + message);
return;
}
string msg2 = intent.Extras.GetString("msg");
string notititle = intent.Extras.GetString("notititle");
if (!string.IsNullOrEmpty(msg2))
{
createNotification(notititle, msg2);
return;
}
// createNotification("PushSharp-GCM Msg Rec'd", "Message Received for C2DM-Sharp... Tap to View!");
//createNotification("Unknown message details", msg.ToString());
}
protected override bool OnRecoverableError (Context context, string errorId)
{
Log.Warn(PushHandlerBroadcastReceiver.TAG, "Recoverable Error: " + errorId);
return base.OnRecoverableError (context, errorId);
}
protected override void OnError (Context context, string errorId)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, "GCM Error: " + errorId);
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show ui
Intent uiIntent = new Intent();
uiIntent.SetClass(this, typeof(dealsview));
uiIntent.PutExtra("contentID", "todaydeals");
uiIntent.PutExtra("contentName", "");
uiIntent.PutExtra("isSale", "");
//Create the notification
var notification = new Notification(Resource.Drawable.Icon, title);
//Auto cancel will remove the notification once the user touches it
notification.Flags = NotificationFlags.AutoCancel;
notification.Defaults = NotificationDefaults.All;
//Set the notification info
//we use the pending intent, passing our ui intent over which will get called
//when the notification is tapped.
notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0));
//Show the notification
notificationManager.Notify(1, notification);
}
}
}
Permission are tags in the Manifest file that has XML data. While compiling DVM checks the Manifest file for permissions and other necessary data to package in the .apk. You should always follow the same format.
Please have a look at this tutorial for more information.

Integrating AspNet.WebApi with AspNet.SignalR

I want to integrate Microsoft.AspNet.SignalR version="2.1.2" with Microsoft.AspNet.WebApi version="5.2.2" so that the API can communicate in REAL-TIME. I found one VERY NICE SAMPLE that implements/works exactly the same way I want, but the sample uses jquery.signalR-0.5.0.js. Some of the earlier implementations have changed, and so far here is what I have done in a failed effort to upgrade the solution to use the latest signalr, asp.net web api and owin.
I left the Hub as it is
using SignalR.Hubs;
namespace NdcDemo.Hubs
{
// This hub has no inbound APIs, since all inbound communication is done
// via the HTTP API. It's here for clients which want to get continuous
// notification of changes to the ToDo database.
[HubName("todo")]
public class ToDoHub : Hub { }
}
I also left the ApiControllerWithHub class as it is
using System;
using System.Web.Http;
using SignalR;
using SignalR.Hubs;
namespace NdcDemo.Controllers
{
public abstract class ApiControllerWithHub<THub> : ApiController
where THub : IHub
{
Lazy<IHubContext> hub = new Lazy<IHubContext>(
() => GlobalHost.ConnectionManager.GetHubContext<THub>()
);
protected IHubContext Hub
{
get { return hub.Value; }
}
}
}
For the ToDoController, I changed the
Hub.Clients.addItem(item), Hub.Clients.updateItem(toUpdate),
Hub.Clients.deleteItem(id)
to
Hub.Clients.All.addItem(item), Hub.Clients.All.updateItem(toUpdate),
Hub.Clients.All.deleteItem(id)
and this is now the full ToDoController class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
using NdcDemo.Hubs;
using NdcDemo.Models;
namespace NdcDemo.Controllers
{
[InvalidModelStateFilter]
public class ToDoController : ApiControllerWithHub<ToDoHub>
{
private static List<ToDoItem> db = new List<ToDoItem>
{
new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
new ToDoItem { ID = 1, Title = "Wash the car" },
new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
};
private static int lastId = db.Max(tdi => tdi.ID);
public IEnumerable<ToDoItem> GetToDoItems()
{
lock (db)
return db.ToArray();
}
public ToDoItem GetToDoItem(int id)
{
lock (db)
{
var item = db.SingleOrDefault(i => i.ID == id);
if (item == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
return item;
}
}
public HttpResponseMessage PostNewToDoItem(ToDoItem item)
{
lock (db)
{
// Add item to the "database"
item.ID = Interlocked.Increment(ref lastId);
db.Add(item);
// Notify the connected clients
Hub.Clients.addItem(item);
// Return the new item, inside a 201 response
var response = Request.CreateResponse(HttpStatusCode.Created, item);
string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
response.Headers.Location = new Uri(link);
return response;
}
}
public ToDoItem PutUpdatedToDoItem(int id, ToDoItem item)
{
lock (db)
{
// Find the existing item
var toUpdate = db.SingleOrDefault(i => i.ID == id);
if (toUpdate == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
// Update the editable fields and save back to the "database"
toUpdate.Title = item.Title;
toUpdate.Finished = item.Finished;
// Notify the connected clients
Hub.Clients.updateItem(toUpdate);
// Return the updated item
return toUpdate;
}
}
public HttpResponseMessage DeleteToDoItem(int id)
{
lock (db)
{
int removeCount = db.RemoveAll(i => i.ID == id);
if (removeCount <= 0)
return Request.CreateResponse(HttpStatusCode.NotFound);
// Notify the connected clients
Hub.Clients.deleteItem(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
}
}
And then I put app.MapSignalR();
But the demo doesn't work...the API doesn't contact clients...Where am I going wrong?
I would still appreciate any more simpler recommendations based on this solution.
Solution from OP.
Answer:
After a a cup of camomile tea, i found out that the clients had to include the KEYWORD CLIENT before the dynamic methods in Todo.js... So, here is what that needs to be modified so that the sample works
hub.client.addItem = function (item) {
alert("i just received something...");
viewModel.add(item.ID, item.Title, item.Finished);
};
hub.client.deleteItem = function (id) {
viewModel.remove(id);
};
hub.client.updateItem = function (item) {
viewModel.update(item.ID, item.Title, item.Finished);
};
And it works!

What is the TPL equivalent of rx's Observable.FromEventPattern?

In rx you can write :
var oe = Observable.FromEventPattern<SqlNotificationEventArgs>(sqlDep, "OnChange");
and then subscribe to the observable to convert the OnChange event on the sqlDep object into an observable.
Similarily, how can you create a Task from a C# event using the Task Parallel Library ?
EDIT: clarification
The solution pointed by Drew and then written explicitely by user375487 works for a single event. As soon as the task finished ... well it is finished.
The observable event is able to trigger again at any time. It is can be seen as an observable stream. A kind of ISourceBlock in the TPL Dataflow. But in the doc http://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx there is no example of ISourceBlock.
I eventually found a forum post explaining how to do that: http://social.msdn.microsoft.com/Forums/en/tpldataflow/thread/a10c4cb6-868e-41c5-b8cf-d122b514db0e
public static ISourceBlock CreateSourceBlock(
Action,Action,Action,ISourceBlock> executor)
{
var bb = new BufferBlock();
executor(t => bb.Post(t), () => bb.Complete(), e => bb.Fault(e), bb);
return bb;
}
//Remark the async delegate which defers the subscription to the hot source.
var sourceBlock = CreateSourceBlock<SomeArgs>(async (post, complete, fault, bb) =>
{
var eventHandlerToSource = (s,args) => post(args);
publisher.OnEvent += eventHandlerToSource;
bb.Complete.ContinueWith(_ => publisher.OnEvent -= eventHandlerToSource);
});
I've not tryed the above code. There may be a mismatch between the async delegate and the definition of CreateSourceBlock.
There is no direct equivalent for the Event Asynchronous Pattern (EAP) baked into the TPL. What you need to do is using a TaskCompletionSource<T> that you signal yourself in the event handler. Check out this section on MSDN for an example of what that would look like which uses WebClient::DownloadStringAsync to demonstrate the pattern.
You can use TaskCompletionSource.
public static class TaskFromEvent
{
public static Task<TArgs> Create<TArgs>(object obj, string eventName)
where TArgs : EventArgs
{
var completionSource = new TaskCompletionSource<TArgs>();
EventHandler<TArgs> handler = null;
handler = new EventHandler<TArgs>((sender, args) =>
{
completionSource.SetResult(args);
obj.GetType().GetEvent(eventName).RemoveEventHandler(obj, handler);
});
obj.GetType().GetEvent(eventName).AddEventHandler(obj, handler);
return completionSource.Task;
}
}
Example usage:
public class Publisher
{
public event EventHandler<EventArgs> Event;
public void FireEvent()
{
if (this.Event != null)
Event(this, new EventArgs());
}
}
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();
var task = TaskFromEvent.Create<EventArgs>(publisher, "Event").ContinueWith(e => Console.WriteLine("The event has fired."));
publisher.FireEvent();
Console.ReadKey();
}
}
EDIT Based on your clarification, here is an example of how to achieve your goal with TPL DataFlow.
public class EventSource
{
public static ISourceBlock<TArgs> Create<TArgs>(object obj, string eventName)
where TArgs : EventArgs
{
BufferBlock<TArgs> buffer = new BufferBlock<TArgs>();
EventHandler<TArgs> handler = null;
handler = new EventHandler<TArgs>((sender, args) =>
{
buffer.Post(args);
});
buffer.Completion.ContinueWith(c =>
{
Console.WriteLine("Unsubscribed from event");
obj.GetType().GetEvent(eventName).RemoveEventHandler(obj, handler);
});
obj.GetType().GetEvent(eventName).AddEventHandler(obj, handler);
return buffer;
}
}
public class Publisher
{
public event EventHandler<EventArgs> Event;
public void FireEvent()
{
if (this.Event != null)
Event(this, new EventArgs());
}
}
class Program
{
static void Main(string[] args)
{
var publisher = new Publisher();
var source = EventSource.Create<EventArgs>(publisher, "Event");
source.LinkTo(new ActionBlock<EventArgs>(e => Console.WriteLine("New event!")));
Console.WriteLine("Type 'q' to exit");
char key = (char)0;
while (true)
{
key = Console.ReadKey().KeyChar;
Console.WriteLine();
if (key == 'q') break;
publisher.FireEvent();
}
source.Complete();
Console.ReadKey();
}
}

Resources