NSUrlSession photo upload task to AzureFunction inside BGTaskScheduler does not get executed when iOS charger cable is unplugged on iOS 14.4 - ios

We are working on a Xamarin Forms app that is supposed to upload photos to API in the background. The app is being custom-made for a client by their request, so they will set their phones to whatever permissions need to be set.
Below works fine if the charging cable is plugged in.
I am using BGTaskScheduler (iOS13+) and queuing both types of tasks (BGProcessingTaskRequest and BGAppRefreshTaskRequest) so that if the cable plugged in it would fire off BGProcessingTaskRequest and if not it would wait for BGAppRefreshTaskRequest to get its processing time.
I have added RefreshTaskId and UploadTaskId to Info.plist
AppDelegate.cs in iOS project looks following
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));
return base.FinishedLaunching(app, options);
}
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
Console.WriteLine("HandleEventsForBackgroundUrl");
BackgroundSessionCompletionHandler = completionHandler;
}
public override void OnActivated(UIApplication application)
{
Console.WriteLine("OnActivated");
}
public override void OnResignActivation(UIApplication application)
{
Console.WriteLine("OnResignActivation");
}
private void HandleAppRefresh(BGAppRefreshTask task)
{
HandleUpload(task);
}
public override void DidEnterBackground(UIApplication application)
{
ScheduleUpload();
}
private void HandleUpload(BGTask task)
{
var uploadService = new UploadService();
uploadService.EnqueueUpload();
task.SetTaskCompleted(true);
}
private void ScheduleUpload()
{
var upload = new BGProcessingTaskRequest(UploadTaskId)
{
RequiresNetworkConnectivity = true,
RequiresExternalPower = false
};
BGTaskScheduler.Shared.Submit(upload, out NSError error);
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (error != null)
Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
if (refreshError != null)
Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
}
The mechanism that does the upload UploadService is using NSUrlSession, it also writes a temporary file to use CreateUploadTask(request, NSUrl.FromFilename(tempFileName)) that is supposed to work in the background, whole mechanism looks following:
public NSUrlSession uploadSession;
public async void EnqueueUpload()
{
var accountsTask = await App.PCA.GetAccountsAsync();
var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
.ExecuteAsync();
if (uploadSession == null)
uploadSession = InitBackgroundSession(authResult.AccessToken);
var datastore = DependencyService.Get<IDataStore<Upload>>();
var uploads = await datastore.GetUnuploaded();
foreach (var unUploaded in uploads)
{
try
{
string folder = unUploaded.Description;
string subfolder = unUploaded.Category;
if (string.IsNullOrEmpty(folder) || string.IsNullOrEmpty(subfolder))
continue;
var uploadDto = new Dtos.Upload
{
FolderName = folder,
SubFolderName = subfolder,
Image = GetImageAsBase64(unUploaded.ImagePath)
};
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = Path.GetFileName(unUploaded.ImagePath);
var tempFileName = Path.Combine(documents, $"{fileName}.txt");
string stringContent = await new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json").ReadAsStringAsync();
await File.WriteAllTextAsync(tempFileName, stringContent);
using (var url = NSUrl.FromString(UploadUrlString))
using (var request = new NSMutableUrlRequest(url)
{
HttpMethod = "POST",
})
{
request.Headers.SetValueForKey(NSObject.FromObject("application/json"), new NSString("Content-type"));
try
{
uploadSession.CreateUploadTask(request, NSUrl.FromFilename(tempFileName));
}
catch (Exception e)
{
Console.WriteLine($"NSMutableUrlRequest failed {e.Message}");
}
}
}
catch (Exception e)
{
if (e.Message.Contains("Could not find a part of the path"))
{
await datastore.DeleteItemAsync(unUploaded.Id);
Console.WriteLine($"deleted");
}
Console.WriteLine($"uploadStore failed {e.Message}");
}
}
}
private string GetImageAsBase64(string path)
{
using (var reader = new StreamReader(path))
using (MemoryStream ms = new MemoryStream())
{
reader.BaseStream.CopyTo(ms);
return Convert.ToBase64String(ms.ToArray());
}
}
public NSUrlSession InitBackgroundSession(string authToken = null, IDataStore<Upload> dataStore = null)
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier))
{
configuration.AllowsCellularAccess = true;
configuration.Discretionary = false;
configuration.AllowsConstrainedNetworkAccess = true;
configuration.AllowsExpensiveNetworkAccess = true;
if (string.IsNullOrWhiteSpace(authToken) == false)
{
configuration.HttpAdditionalHeaders = NSDictionary.FromObjectsAndKeys(new string[] { $"Bearer {authToken}" }, new string[] { "Authorization" });
}
return NSUrlSession.FromConfiguration(configuration, new UploadDelegate(dataStore), null);
}
}
}
public class UploadDelegate : NSUrlSessionTaskDelegate, INSUrlSessionDelegate
{
public IDataStore<Upload> Datastore { get; }
public UploadDelegate(IDataStore<Upload> datastore)
{
this.Datastore = datastore;
}
public override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
{
Console.WriteLine(string.Format("DidCompleteWithError TaskId: {0}{1}", task.TaskIdentifier, (error == null ? "" : " Error: " + error.Description)));
if (error == null)
{
ProcessCompletedTask(task);
}
}
public void ProcessCompletedTask(NSUrlSessionTask sessionTask)
{
try
{
Console.WriteLine(string.Format("Task ID: {0}, State: {1}, Response: {2}", sessionTask.TaskIdentifier, sessionTask.State, sessionTask.Response));
if (sessionTask.Response == null || sessionTask.Response.ToString() == "")
{
Console.WriteLine("ProcessCompletedTask no response...");
}
else
{
var resp = (NSHttpUrlResponse)sessionTask.Response;
Console.WriteLine("ProcessCompletedTask got response...");
if (sessionTask.State == NSUrlSessionTaskState.Completed && resp.StatusCode == 201)
{
Console.WriteLine("201");
}
}
}
catch (Exception ex)
{
Console.WriteLine("ProcessCompletedTask Ex: {0}", ex.Message);
}
}
public override void DidBecomeInvalid(NSUrlSession session, NSError error)
{
Console.WriteLine("DidBecomeInvalid" + (error == null ? "undefined" : error.Description));
}
public override void DidFinishEventsForBackgroundSession(NSUrlSession session)
{
Console.WriteLine("DidFinishEventsForBackgroundSession");
}
public override void DidSendBodyData(NSUrlSession session, NSUrlSessionTask task, long bytesSent, long totalBytesSent, long totalBytesExpectedToSend)
{
}
}
Everything works if the iOS charger cable is plugged in, however, if it isn't nothing fires. I have a network debugging set up with plenty of logging into the console, and I can see that nothing happens on iPhone.
"Low power mode" setting on iOS is off.
I have watched Background execution demystified and I am setting session configuration.Discretionary = false;
How do I make the NSUrlSession upload task to fire when iOS charger cable is unplugged on iOS 14.4?

Following works without charging cable:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public Action BackgroundSessionCompletionHandler { get; set; }
public static string UploadTaskId { get; } = "XXX.upload";
public static NSString UploadSuccessNotificationName { get; } = new NSString($"{UploadTaskId}.success");
public static string RefreshTaskId { get; } = "XXX.refresh";
public static NSString RefreshSuccessNotificationName { get; } = new NSString($"{RefreshTaskId}.success");
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));
return base.FinishedLaunching(app, options);
}
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
return true;
}
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
Console.WriteLine("HandleEventsForBackgroundUrl");
BackgroundSessionCompletionHandler = completionHandler;
}
public override void OnActivated(UIApplication application)
{
Console.WriteLine("OnActivated");
var uploadService = new UploadService();
uploadService.EnqueueUpload();
}
public override void OnResignActivation(UIApplication application)
{
Console.WriteLine("OnResignActivation");
}
private void HandleAppRefresh(BGAppRefreshTask task)
{
task.ExpirationHandler = () =>
{
Console.WriteLine("BGAppRefreshTask ExpirationHandler");
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (refreshError != null)
Console.WriteLine($"BGAppRefreshTask ExpirationHandler Could not schedule BGAppRefreshTask: {refreshError}");
};
HandleUpload(task);
}
public override void DidEnterBackground(UIApplication application) => ScheduleUpload();
private void HandleUpload(BGTask task)
{
Console.WriteLine("HandleUpload");
var uploadService = new UploadService();
uploadService.EnqueueUpload();
task.SetTaskCompleted(true);
}
private void ScheduleUpload()
{
Console.WriteLine("ScheduleUpload");
var upload = new BGProcessingTaskRequest(UploadTaskId)
{
RequiresNetworkConnectivity = true,
RequiresExternalPower = false
};
BGTaskScheduler.Shared.Submit(upload, out NSError error);
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (error != null)
Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
if (refreshError != null)
Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
}
}
then Upload service:
public class UploadService : IUploadService
{
private const string uploadUrlString = "https://Yadyyadyyada";
public async void EnqueueUpload()
{
var accountsTask = await App.PCA.GetAccountsAsync();
var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
.ExecuteAsync();
try
{
var uploadDto = new object();
var message = new HttpRequestMessage(HttpMethod.Post, uploadUrlString);
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
message.Content = new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(message);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
}
}
catch (Exception e)
{
Console.WriteLine($"EnqueueUpload {e.Message}");
}
}
}

Related

Can not discover service

Im my Xamarin.iOS app, I am advertising a service UUID, and scanning for ALL service UUIDs at the same time, with BLE. Here is my code:
[Register("AppDelegate")]
public class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
private CBCentralManager _cbCentralManager;
private CBPeripheralManager _cbPeripheralManager;
private System.Threading.Timer _timer = null;
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public event EventHandler<string> Log;
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new MobileDemo.App());
if (_cbCentralManager == null)
{
_cbCentralManager = new CBCentralManager();
}
if(_cbPeripheralManager == null)
{
_cbPeripheralManager = new CBPeripheralManager();
}
_cbPeripheralManager.AdvertisingStarted -= CBPeripheralManager_AdvertisingStarted;
_cbPeripheralManager.AdvertisingStarted += CBPeripheralManager_AdvertisingStarted;
_cbCentralManager.DiscoveredPeripheral -= CBCentralManager_DiscoveredPeripheral;
_cbCentralManager.DiscoveredPeripheral += CBCentralManager_DiscoveredPeripheral;
_cbCentralManager.UpdatedState -= CBCentralManager_UpdatedState;
_cbCentralManager.UpdatedState += CBCentralManager_UpdatedState;
return base.FinishedLaunching(application, launchOptions);
}
private void CBPeripheralManager_AdvertisingStarted(object sender, NSErrorEventArgs e)
{
Log?.Invoke(null, $"advertising started: {e?.Error?.ToString()}");
}
private void CBCentralManager_UpdatedState(object sender, EventArgs e)
{
Log?.Invoke(null, $"CB update state: {_cbCentralManager.State}");
if (_cbCentralManager.State == CBCentralManagerState.PoweredOn)
{
_timer = null;
_timer = new System.Threading.Timer((obj) =>
{
Scan();
},
null, 1000, 5000);
// Wait for 2 seconds before start advertising, or it won't work sometimes
System.Threading.Timer _advertise = null;
_advertise = new System.Threading.Timer((obj) =>
{
StartAdvertisingOptions advOptions = new StartAdvertisingOptions
{
ServicesUUID = new CBUUID[] { CBUUID.FromString("12345678-1111-1111-1111-000000000000")}
};
_cbPeripheralManager.StartAdvertising(advOptions);
_advertise.Dispose();
_advertise = null;
},
null, 2000, 0);
}
else
{
_cbCentralManager.StopScan();
_cbPeripheralManager.StopAdvertising();
}
}
private async void Scan()
{
Log?.Invoke(null, $"Scanning...");
_cbCentralManager.ScanForPeripherals(new CBUUID[0]); // Do NOT pass null to this method. It won't work. Pass empty array instead
await Task.Delay(2000);
Log?.Invoke(null, $"Stoping scan...");
_cbCentralManager.StopScan();
}
private void CBCentralManager_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e)
{
Log?.Invoke(null, $"Peripheral discovered");
GetService(e.Peripheral);
}
private async Task WaitForTaskWithTimeout(Task task, int timeout)
{
await Task.WhenAny(task, Task.Delay(timeout));
if (!task.IsCompleted)
{
throw new TimeoutException();
}
}
public async Task GetService(CBPeripheral peripheral)
{
var service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
return;
}
var taskCompletion = new TaskCompletionSource<bool>();
var task = taskCompletion.Task;
EventHandler<NSErrorEventArgs> handler = (s, e) =>
{
service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
taskCompletion.SetResult(true);
}
else
{
Log?.Invoke(null, $"no service");
}
};
try
{
peripheral.DiscoveredService += handler;
peripheral.DiscoverServices();
await this.WaitForTaskWithTimeout(task, 2000);
service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
}
else
{
Log?.Invoke(null, $"no service");
}
}
finally
{
peripheral.DiscoveredService -= handler;
}
}
public CBService GetServiceIfDiscovered(CBPeripheral peripheral)
{
return peripheral.Services?.FirstOrDefault();
}
}
I can discover the peripherals, but my DiscoveredService handler never gets called. I know the advertisement is working because I can discover the service UUID on the Android version of this same app (different implementation). But I can't discover the service on another iOS device. What am I doing wrong?
EDIT
Since I found out that I actually have to connect to the device before discovering its services, I wrote a manager class to help connect and discover the service UUID that I am advertising from a different device:
public class BleServiceManager
{
private readonly CBCentralManager _cbCentralManager;
private bool _isGettingService;
private bool _isConnectingToPeripheral;
private Queue<CBPeripheral> _disconnectedPeripherals = new Queue<CBPeripheral>();
private Queue<CBPeripheral> _connectedPeripherals = new Queue<CBPeripheral>();
public event EventHandler<string> Log;
public event EventHandler<string> FoundMyService;
public BleServiceManager(CBCentralManager cbCentralManager)
{
_cbCentralManager = cbCentralManager;
}
public void FindServiceForPeripheral(CBPeripheral peripheral)
{
if (peripheral.State == CBPeripheralState.Disconnected)
{
_disconnectedPeripherals.Enqueue(peripheral);
if (!_isConnectingToPeripheral)
{
ConnectToNextPeripheral();
}
}
}
private void ConnectToNextPeripheral()
{
if (_disconnectedPeripherals.Any())
{
_isConnectingToPeripheral = true;
var p = _disconnectedPeripherals.Dequeue();
if (p.State == CBPeripheralState.Disconnected)
{
ConnectTo(p);
}
else
{
_isConnectingToPeripheral = false;
}
}
else
{
_isConnectingToPeripheral = false;
}
}
private async Task ConnectTo(CBPeripheral peripheral)
{
var taskCompletion = new TaskCompletionSource<bool>();
var task = taskCompletion.Task;
EventHandler<CBPeripheralEventArgs> connectedHandler = (s, e) =>
{
if (e.Peripheral?.State == CBPeripheralState.Connected && peripheral.Identifier?.ToString() == e.Peripheral.Identifier?.ToString())
{
_connectedPeripherals.Enqueue(peripheral);
taskCompletion.SetResult(true);
}
};
try
{
_cbCentralManager.ConnectedPeripheral += connectedHandler;
_cbCentralManager.ConnectPeripheral(peripheral);
await this.WaitForTaskWithTimeout(task, 2000);
Log?.Invoke(null, $"Bluetooth device connected = {peripheral.Name}");
if (!_isGettingService)
{
DiscoverServicesOnNextConnectedPeripheral();
}
}
catch (TimeoutException e)
{
Disconnect(peripheral);
}
finally
{
_cbCentralManager.ConnectedPeripheral -= connectedHandler;
ConnectToNextPeripheral();
}
}
private void DiscoverServicesOnNextConnectedPeripheral()
{
if (_connectedPeripherals.Any())
{
_isGettingService = true;
var p = _connectedPeripherals.Dequeue();
GetService(p);
}
else
{
_isGettingService = false;
}
}
private async Task GetService(CBPeripheral peripheral)
{
var service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
Disconnect(peripheral);
FoundMyService?.Invoke(null, service.UUID.Uuid);
DiscoverServicesOnNextConnectedPeripheral();
return;
}
var taskCompletion = new TaskCompletionSource<bool>();
var task = taskCompletion.Task;
EventHandler<NSErrorEventArgs> handler = (s, e) =>
{
service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
FoundMyService?.Invoke(null, service.UUID.Uuid);
taskCompletion.SetResult(true);
}
else
{
Log?.Invoke(null, $"no service");
}
};
try
{
peripheral.DiscoveredService += handler;
peripheral.DiscoverServices();
await this.WaitForTaskWithTimeout(task, 10000);
service = this.GetServiceIfDiscovered(peripheral);
if (service != null)
{
Log?.Invoke(null, $"service found");
FoundMyService?.Invoke(null, service.UUID.Uuid);
}
else
{
Log?.Invoke(null, $"no service");
}
}
catch(TimeoutException e)
{
}
finally
{
peripheral.DiscoveredService -= handler;
Disconnect(peripheral);
DiscoverServicesOnNextConnectedPeripheral();
}
}
private CBService GetServiceIfDiscovered(CBPeripheral peripheral)
{
return peripheral.Services?.FirstOrDefault(x => x.UUID?.Uuid?.StartsWith("12345678") == true); // the service uuid that I am advertising starts with 12345678
}
private void Disconnect(CBPeripheral peripheral)
{
_cbCentralManager.CancelPeripheralConnection(peripheral);
}
private async Task WaitForTaskWithTimeout(Task task, int timeout)
{
await Task.WhenAny(task, Task.Delay(timeout));
if (!task.IsCompleted)
{
throw new TimeoutException();
}
}
}
I call FindServiceForPeripheral from my AppDelegate as soon as I discover a peripheral. It's event-driven and made it so GetService is never called more than 1 at a time. It finds other services (like for the battery) but never the service that I am advertising.
It was actually a lot more simple than I thought. To advertise a service UUID:
StartAdvertisingOptions advOptions = new StartAdvertisingOptions
{
ServicesUUID = new CBUUID[] { CBUUID.FromString("yourUuidHere") }
};
_cbPeripheralManager.StartAdvertising(advOptions);
To discover the same UUID on another device:
_cbCentralManager.DiscoveredPeripheral += CBCentralManager_DiscoveredPeripheral;
private void CBCentralManager_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e)
{
var key = new NSString("kCBAdvDataServiceUUIDs");
foreach(var x in e.AdvertisementData.Keys)
{
if(x is NSString && (NSString)x == key)
{
var y = e.AdvertisementData.ValueForKey((NSString)x);
if(y.ToString().Contains("yourUuidHere"))
{
// found it
}
}
}
}
I'm guessing the DiscoverServices() call is to find GATT services, not your own advertisement.

How to live notification in MVC with SignalR?

I'm trying to make live notification using signalR. My project is running on localhost. But I don't see my notification when I set webconfig server-side. (although I did it with signalR)
When I run the 'internet' part of Chrome 's check item, I see that the request does not fall. how do I make this problem?
ajax code;
function updateNotification() {
$('#notiContent').empty();
$('#notiContent').append($('<li>Yükleniyor...</li>'));
$.ajax({
type: 'GET',
datatype : JSON,
contentType: 'application/json; charset=utf-8',
url: '/notification/GetNotificationFlows',
success: function (response) {
$('#notiContent').empty();
if (response.length == 0) {
$('#notiContent').append($('<li>Data yok..</li>'));
}
$.each(response, function (index, value) {
$('#notiContent').append($('<li>Yeni kişi : ' + value.flowName + ' (' + value.flowPhone + ') eklendi.</li>'));
});
},
error: function (error) {
console.log(error);
}
})
}
Global.asax;
string con = ConfigurationManager.ConnectionStrings["sqlConString"].ConnectionString;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
SqlDependency.Start(con);
}
protected void Session_Start(object sender, EventArgs e)
{
NotificationComponent NC = new NotificationComponent();
var currentTime = DateTime.Now;
HttpContext.Current.Session["LastUpdated"] = currentTime;
NC.RegisterNotification(currentTime);
}
protected void Application_End()
{
//here we will stop Sql Dependency
SqlDependency.Stop(con);
}
}
Notification component
public void RegisterNotification(DateTime currentTime)
{
string conStr = ConfigurationManager.ConnectionStrings["sqlConString"].ConnectionString;
string sqlCommand = #"SELECT [flowId],[flowName],[flowEMail],[flowPhone],[kaynakId] from [dbo].[flow] where [createDate] > #createDate";
//you can notice here I have added table name like this [dbo].[Contacts] with [dbo], its mendatory when you use Sql Dependency
using (SqlConnection con = new SqlConnection(conStr))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
cmd.Parameters.AddWithValue("#createDate", currentTime);
if (con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency sqlDep = new SqlDependency(cmd);
sqlDep.OnChange += sqlDep_OnChange;
//we must have to execute the command here
using (SqlDataReader reader = cmd.ExecuteReader())
{
// nothing need to add here now
}
}
}
void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
SqlDependency sqlDep = sender as SqlDependency;
sqlDep.OnChange -= sqlDep_OnChange;
//from here we will send notification message to client
var notificationHub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
notificationHub.Clients.All.notify("eklendi.");
//re-register notification
RegisterNotification(DateTime.Now);
}
}
public List<flow> GetFlows(DateTime afterDate)
{
using (smartCMSEntities dc = new smartCMSEntities())
{
return dc.flow.Where(a => a.createDate > afterDate).OrderByDescending(a => a.createDate).ToList();
}
}
Notification Controller
public JsonResult GetNotificationFlows()
{
var notificationRegisterTime = Session["LastUpdated"] != null ? Convert.ToDateTime(Session["LastUpdated"]) : DateTime.Now;
NotificationComponent NC = new NotificationComponent();
var list = NC.GetFlows(notificationRegisterTime);
Session["LastUpdate"] = DateTime.Now;
return new JsonResult { Data = list, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
Notification Hub
public class NotificationHub : Hub
{
//public void Hello()
//{
// Clients.All.hello();
//}
}
SQL (for sql dependency)
ALTER DATABASE [db_name] SET ENABLE_BROKER with rollback immediate;
I had the same problem you need to create your function inside your Hub.
Let say
public class NotificationHub : Hub
{
public static void Send()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.All.displayStatus();
}
}
And call it in your html
function updateNotificationCount() {
$('span.count').show();
var count = 0;
count = parseInt($('span.count').html()) || 0;
count++;
$('span.noti').css("color", "white");
// $('span.count').css({ "background-color": "red", "color": "white" });
$('span.count').html(count);
}
// signalr js code for start hub and send receive notification
var hub = $.connection.notificationHub;
// Declare a function on the hub hub so the server can invoke it
hub.client.displayStatus = function () {
updateNotificationCount();
};
// Start the connection
$.connection.hub.start();

Updating image control in Windows Phone 8

I have a HTML5 web app I can view through my mobile devices.
I have an img control that would download an image using an ashx asp.net handler.
I updated via a timer.
I am trying to port this over to a Windows Phone 8.1 app instead.
The image seems to take ages to update (if at all). This is my code:
long tick = DateTime.Now.Ticks;
BitmapImage bmp =new BitmapImage(new Uri("http://my url/Mobile/NewFrame.ashx?b=1a=9A5C3-E1945-3D315-BB43C&c=3&m=1&t=" + tick));
imgFrame1.Source = bmp;
Is this the correct way?
this is the full code:
private async void LogIn()
{
using (var client = new HttpClient())
{
var resp = await client.PostAsJsonAsync("http://my url/UserManagement/Login.aspx/Test",
new { username = "", password = "", hubuserid = hubuserid });
var str = await resp.Content.ReadAsStringAsync();
var jsonObj = JsonConvert.DeserializeObject<UserLogIn>(str);
if (jsonObj.d.Success)
{
UpdateConnectionState("Logged In");
}
else
{
UpdateConnectionState("Not Logged In");
}
}
}
public class D
{
public string __type { get; set; }
public bool Success { get; set; }
}
public class UserLogIn
{
public D d { get; set; }
}
private string hubuserid = "";
public string Uptime { get; set; }
private byte ImageIsLoaded = 1;
private async void UpdateTime(int data)
{
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
try
{
txtInfo.Text = data.ToString();
if (ImageIsLoaded == 1)
{
ImageIsLoaded = 0;
long tick = DateTime.Now.Ticks;
BitmapImage bi = new BitmapImage(new Uri("http://www.informedmotion.co.uk/Mobile/NewFrame.ashx?b=1a=9A5C3-E1945-3D315-BB43C&c=3&m=1&t=" + tick, UriKind.Absolute));
bi.DownloadProgress += bi_DownloadProgress;
bi.ImageOpened += bi_ImageOpened; }
}
catch (Exception ex)
{
txtInfo.Text = ex.ToString();
}
});
}
void bi_DownloadProgress(object sender, DownloadProgressEventArgs e)
{
//throw new NotImplementedException();
}
void bi_ImageOpened(object sender, RoutedEventArgs e)
{
ImageIsLoaded = 1;
imgFrame1.Source = (BitmapImage)sender;
}
private void imgFrame1_ImageOpened(object sender, RoutedEventArgs e)
{
ImageIsLoaded = 1;
}
private void imgFrame1_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
ImageIsLoaded = 1;
}
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
imgFrame1.ImageFailed += imgFrame1_ImageFailed;
imgFrame1.ImageOpened += imgFrame1_ImageOpened;
ConnectToHub();
}
private void ConnectToHub()
{
proxy.On<int>("broadcastMessage", data =>
{
UpdateTime(data);
});
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
UpdateConnectionState("Not Connected");
ConnectToHub();
}
else
{
UpdateConnectionState(string.Format("Success! Connected with client connection id {0}", connection.ConnectionId));
hubuserid = connection.ConnectionId;
LogIn();
}
});
connection.Error += ex =>
{
UpdateConnectionState(string.Format("An error occurred {0}", ex.Message));
};
connection.Closed += () =>
{
UpdateConnectionState(string.Format("Connection with client id {0} closed", connection.ConnectionId));
ConnectToHub();
};
connection.Reconnected += () =>
{
//LogIn();
UpdateConnectionState("The connection was re-established");
};
}
Windows.UI.Core.CoreDispatcher dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
async void UpdateConnectionState(string state)
{
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
try{
txtInfo.Text = state;
}
catch (Exception ex)
{
txtInfo.Text = ex.ToString();
}
});
}
static HubConnection connection = new HubConnection("http://www.informedmotion.co.uk/");
IHubProxy proxy = connection.CreateHubProxy("ChatHub");
If you're going to download the image, then you probably want to hooked the
Image.DownloadProgress event
Image.ImageOpened event
ImageOpened will fire once the download is complete, so at that moment you can set the .Source to it.
While it is downloading (if it's a huge image) you can either show the previous image or a place holder image (with progress bar maybe?)
BitmapImage bi = new BitmapImage(new Uri("http://www.google.com/myimage.bmp", UriKind.Absolute));
bi.DownloadProgress += bi_DownloadProgress;
bi.ImageOpened += bi_ImageOpened;
hiddenImage.Source = bi; // we need to set it to an element in the visual tree so the
// events will fire, we're going to use the hiddenImage
void bi_DownloadProgress(object sender, DownloadProgressEventArgs e)
{
throw new NotImplementedException();
}
void bi_ImageOpened(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
<!-- myImage is your image that you use to show stuff -->
<!-- hiddenImage is the image we use to fire the event -->
<Image x:Name="myImage"></Image>
<Image x:Name="hiddenImage" Visibility="Collapsed"></Image>

BlackBerry read json string from an URL

I tried to read a json string loading from an URL, but I could not find complete code sample. Can any one provide or point me to a complete client code. I'm newer to BB development.
this is what I have done but still can't get it work please help me.
Thanks!
To read and parse data from an URL you need to implement two routines. First one of them will handle reading data from the specified URL over HTTP connection, and the second one will parse the data.
Check the following application HttpUrlReaderDemoApp, which will first read the data from specified URL and then parse the retrieved data.
URL used to retrieve data: http://codeincloud.tk/json_android_example.php
Sample data format: {"name":"Froyo", "version":"Android 2.2"}
Classes:
HttpUrlReaderDemoApp - UiApplication instance
HttpResponseListener - Interface used to notify other classes about HTTP request status
HttpUrlReader - Reads the data from given url
AppMainScreen - MainScreen instance
DataParser - Parse data
DataModel - Data definition
Screenshots:
Request for data
When data retrieved successfully
Parsed data
Implementation:
HttpUrlReaderDemoApp
public class HttpUrlReaderDemoApp extends UiApplication {
public static void main(String[] args) {
HttpUrlReaderDemoApp theApp = new HttpUrlReaderDemoApp();
theApp.enterEventDispatcher();
}
public HttpUrlReaderDemoApp() {
pushScreen(new AppMainScreen("HTTP Url Reader Demo Application"));
}
}
HttpResponseListener
public interface HttpResponseListener {
public void onHttpResponseFail(String message, String url);
public void onHttpResponseSuccess(byte bytes[], String url);
}
HttpUrlReader
public class HttpUrlReader implements Runnable {
private String url;
private HttpResponseListener listener;
public HttpUrlReader(String url, HttpResponseListener listener) {
this.url = url;
this.listener = listener;
}
private String getConncetionDependentUrlSuffix() {
// Not implemented
return "";
}
private void notifySuccess(byte bytes[], String url) {
if (listener != null) {
listener.onHttpResponseSuccess(bytes, url);
}
}
private void notifyFailure(String message, String url) {
if (listener != null) {
listener.onHttpResponseFail(message, url);
}
}
private boolean isValidUrl(String url) {
return (url != null && url.length() > 0);
}
public void run() {
if (!isValidUrl(url) || listener == null) {
String message = "Invalid parameters.";
message += !isValidUrl(url) ? " Invalid url." : "";
message += (listener == null) ? " Invalid HttpResponseListerner instance."
: "";
notifyFailure(message, url);
return;
}
// update URL depending on connection type
url += DeviceInfo.isSimulator() ? ";deviceside=true"
: getConncetionDependentUrlSuffix();
// Open the connection and retrieve the data
try {
HttpConnection httpConn = (HttpConnection) Connector.open(url);
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK) {
InputStream input = httpConn.openInputStream();
byte[] bytes = IOUtilities.streamToBytes(input);
input.close();
notifySuccess(bytes, url);
} else {
notifyFailure("Failed to retrieve data, HTTP response code: "
+ status, url);
return;
}
httpConn.close();
} catch (Exception e) {
notifyFailure("Failed to retrieve data, Exception: ", e.toString());
return;
}
}
}
AppMainScreen
public class AppMainScreen extends MainScreen implements HttpResponseListener {
private final String URL = "http://codeincloud.tk/json_android_example.php";
public AppMainScreen(String title) {
setTitle(title);
}
private MenuItem miReadData = new MenuItem("Read data", 0, 0) {
public void run() {
requestData();
}
};
protected void makeMenu(Menu menu, int instance) {
menu.add(miReadData);
super.makeMenu(menu, instance);
}
public void close() {
super.close();
}
public void showDialog(final String message) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(message);
}
});
}
private void requestData() {
Thread urlReader = new Thread(new HttpUrlReader(URL, this));
urlReader.start();
showDialog("Request for data from\n \"" + URL + "\"\n started.");
}
public void onHttpResponseFail(String message, String url) {
showDialog("Failure Mesage:\n" + message + "\n\nUrl:\n" + url);
}
public void onHttpResponseSuccess(byte bytes[], String url) {
showDialog("Data retrived from:\n" + url + "\n\nData:\n"
+ new String(bytes));
// now parse response
DataModel dataModel = DataParser.getData(bytes);
if (dataModel == null) {
showDialog("Failed to parse data: " + new String(bytes));
} else {
showDialog("Parsed Data:\nName: " + dataModel.getName()
+ "\nVersion: " + dataModel.getVersion());
}
}
}
DataParser
public class DataParser {
private static final String NAME = "name";
private static final String VERSION = "version";
public static DataModel getData(byte data[]) {
String rawData = new String(data);
DataModel dataModel = new DataModel();
try {
JSONObject jsonObj = new JSONObject(rawData);
if (jsonObj.has(NAME)) {
dataModel.setName(jsonObj.getString(NAME));
}
if (jsonObj.has(VERSION)) {
dataModel.setVersion(jsonObj.getString(VERSION));
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return dataModel;
}
}
DataModel
public class DataModel {
private String name;
private String version;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String model) {
this.version = model;
}
}

Blackberry screen renew with new data

I am developing a Blackberry Application. I have a map in a screen. I want to refresh map's data with new data which i am getting from my web service. I am using BlockingSenderDestination in a Thread. When i request "get data" its return new data. no problem. I am using invokelater function to call my maprefresh function with passing arguments but i got illegalargumentexception.
Any suggestion to solve my problem or any better way to do this?
Here is my code:
public class MyMainScreen extends MainScreen {
RichMapField map;
MyClassList _myclassList;
private String _result2t;
public MyMainScreen(JSONArray jarray)
{
map = MapFactory.getInstance().generateRichMapField();
MapDataModel mapDataModel = map.getModel();
JSONObject json = null;
boolean getdata=false;
for (int i=0;i<jarray.length();i++)
{
try
{
json=jarray.getJSONObject(i);
getdata=true;
}
catch(Exception e)
{
}
if(getdata)
{
try
{
double lat = Double.valueOf(json.getString("LATITUDE")).doubleValue();
double lng = Double.valueOf(json.getString("LONGITUDE")).doubleValue();
String myclassdata= json.getString("myclassdata").toString();
MyClass ben = new MyClass(myclassdata);
_myclassList.addElement(ben);
MapLocation termimapitem = new MapLocation( lat, lng, "","");
mapDataModel.add((Mappable)termimapitem,"1");
}
catch(Exception e)
{
//mesajGoster("Hatalı Veri");
}
}
else
{
//mesajGoster("Listeye Eklenemedi");
}
}
}
private void GetTerminals(String companyNo){
final String companyNoR= companyNo;
Thread t = new Thread(new Runnable()
{
public void run()
{
Message response = null;
String uriStr = "http://webservice";
BlockingSenderDestination bsd = null;
try
{
bsd = (BlockingSenderDestination)
DestinationFactory.getSenderDestination
("o", URI.create(uriStr));
if(bsd == null)
{
bsd =
DestinationFactory.createBlockingSenderDestination
(new Context("o"),
URI.create(uriStr)
);
}
response = bsd.sendReceive();
if(response != null)
{
BSDResponse(response,companyNoR);
}
}
catch(Exception e)
{
}
finally
{
if(bsd != null)
{
bsd.release();
}
}
}
});
t.start();
}
private void BSDResponse(Message msg,final String companyNo)
{
if (msg instanceof ByteMessage)
{
ByteMessage reply = (ByteMessage) msg;
_result2t = (String) reply.getStringPayload();
} else if(msg instanceof StreamMessage)
{
StreamMessage reply = (StreamMessage) msg;
InputStream is = reply.getStreamPayload();
byte[] data = null;
try {
data = net.rim.device.api.io.IOUtilities.streamToBytes(is);
} catch (IOException e) {
// process the error
}
if(data != null)
{
_result2t = new String(data);
}
}
try {
final JSONArray jarray= new JSONArray(_result2t);
final String username=_userName;
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert("The Toolbar i");
Yenile(jarray);
}
});
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void Yenile(JSONArray jarray){
MapDataModel mapDataModel = map.getModel();
mapDataModel.remove("1");
map.getMapField().update(true);
_terminalList = new TerminalList();
map= MapFactory.getInstance().generateRichMapField();
MapDataModel mapDataModel = map.getModel();
JSONObject json = null;
boolean getdata=false;
for (int i=0;i<jarray.length();i++)
{
try
{
json=jarray_terminaller.getJSONObject(i);
getdata=true;
}
catch(Exception e)
{
}
if(getdata)
{
try
{
double lat = Double.valueOf(json.getString("LATITUDE")).doubleValue();
double lng = Double.valueOf(json.getString("LONGITUDE")).doubleValue();
String myclassdata= json.getString("myclassdata").toString();
MyClass ben = new MyClass(myclassdata);
_myclassList.addElement(ben);
MapLocation termimapitem = new MapLocation( lat, lng, "","");
mapDataModel.add((Mappable)termimapitem,"1");
}
catch(Exception e)
{
//mesajGoster("Hatalı Veri");
}
}
else
{
//mesajGoster("Listeye Eklenemedi");
}
}
}
}
To refresh the screen: do like this:
public class LoadingScreen extends MainScreen{
LoadingScreen()
{
createGUI();
}
public void createGUI()
{
//Here you write the code that display on screen;
}}
we know that this is the actual way of creating a screen;
Now if you want to refresh the screen write like below:
deleteAll();
invalidate();
createGUI();//here it creates the screen with new data.
Instead of writing in InvokeLater method better to write the above three lines in run method after Thread.sleep(10000);
If you have any doubts come on stackOverFlow chat room name "Life for Blackberry" for clarify your and our doubts.
I found a solution to my question.
After getting the data i was sending it via new run method:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
MyFunction(jarray);
}});
But i was need to synchronize with main thread. So the solution:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
synchronized(Application.getEventLock()) {
Yenile(jarray);
}
}
});

Resources