I am trying to create a Xamarin.Forms app that will run on both iOS and Android. Eventually I need instances of the app to communicate with each other via Bluetooth, but I'm stuck on getting the iOS side to do anything with Bluetooth. I originally tried to work with Plugin.BluetoothLE and Plugin.BLE, but after a week and a half I was not able to get advertising or scanning to work on either OS with either plugin, so I decided to try implementing simple Bluetooth interaction using the .NET wrappers of the platform APIs, which at least are well documented. I did get scanning to work fine on the Android side. With iOS, though, what I have right now builds just fine, and runs on my iPad without errors, but the DiscoveredPeripheral handler is never called, even though the iPad is just a few inches from the Android tablet and presumably should be able to see the same devices. I have verified this by setting a breakpoint in that method, which is never reached; and when I open the Bluetooth Settings on the iPad to make it discoverable the app version on the Android tablet can see it, so I don't think it's an iPad hardware issue.
It seems obvious that there is simply some part of the process I don't know to do, but it's not obvious (to me) where else to look to find out what it is. Here is the code for the class that interacts with the CBCentralManager (as far as I understand from what I've read, this should include everything necessary to return a list of peripherals):
using MyBluetoothApp.Shared; // for the interfaces and constants
using CoreBluetooth;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
[assembly: Dependency(typeof(MyBluetoothApp.iOS.PeripheralScanner))]
namespace MyBluetoothApp.iOS
{
public class PeripheralScanner : IPeripheralScanner
{
private readonly CBCentralManager manager;
private List<IPeripheral> foundPeripherals;
public PeripheralScanner()
{
this.foundPeripherals = new List<IPeripheral>();
this.manager = new CBCentralManager();
this.manager.DiscoveredPeripheral += this.DiscoveredPeripheral;
this.manager.UpdatedState += this.UpdatedState;
}
public async Task<List<IPeripheral>> ScanForService(string serviceUuid)
{
return await this.ScanForService(serviceUuid, BluetoothConstants.DEFAULT_SCAN_TIMEOUT);
}
public async Task<List<IPeripheral>> ScanForService(string serviceUuid, int duration)
{
CBUUID uuid = CBUUID.FromString(serviceUuid);
//this.manager.ScanForPeripherals(uuid);
this.manager.ScanForPeripherals((CBUUID)null); // For now I'd be happy to see ANY peripherals
await Task.Delay(duration);
this.manager.StopScan();
return this.foundPeripherals;
}
private void DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs args)
{
this.foundPeripherals.Add(new CPeripheral(args.Peripheral));
}
private void UpdatedState(object sender, EventArgs args)
{
CBCentralManagerState state = ((CBCentralManager)sender).State;
if (CBCentralManagerState.PoweredOn != state)
{
throw new Exception(state.ToString());
}
}
}
}
Can anyone point me in the direction of understanding what I'm missing?
EDIT: O...K, I've discovered quite by accident that if I do this in the shared code:
IPeripheralScanner scanner = DependencyService.Get<IPeripheralScanner>();
List<IPeripheral> foundPeripherals = await scanner.ScanForService(BluetoothConstants.VITL_SERVICE_UUID);
twice in a row, it works the second time. I feel both more hopeful and much more confused.
The underlying problem was that in the first instantiation of PeripheralScanner, ScanForService was being called before State was updated. I tried many ways of waiting for that event to be raised so I could be sure the state was PoweredOn, but nothing seemed to work; polling loops simply never reached the desired state, but if I threw an Exception in the UpdatedState handler it was thrown within milliseconds of launch and the state at that time was always PoweredOn. (Breakpoints in that handler caused the debugging to freeze with the output Resolved pending breakpoint, which not even the VS team seems to be able to explain).
Reading some of the Apple developer blogs I found that this situation is most often avoided by having the desired action occur within the UpdatedState handler. It finally soaked into my thick head that I was never seeing any effects from that handler running because the event was being raised and handled on a different thread. I really need to pass the service UUID to the scanning logic, and to interact with a generic List that I can return from ScanForService, so just moving it all to the handler didn't seem like a promising direction. So I created a singleton for flagging the state:
internal sealed class ManagerState // .NET makes singletons easy - Lazy<T> FTW
{
private static readonly Lazy<ManagerState> lazy = new Lazy<ManagerState>(() => new ManagerState());
internal static ManagerState Instance { get { return ManagerState.lazy.Value; } }
internal bool IsPoweredOn { get; set; }
private ManagerState()
{
this.IsPoweredOn = false;
}
}
and update it in the handler:
private void updatedState(object sender, EventArgs args)
{
ManagerState.Instance.IsPoweredOn = CBCentralManagerState.PoweredOn == ((CBCentralManager) sender).State;
}
then poll that at the beginning of ScanForService (in a separate thread each time because, again, I will not see the updates in my base thread):
while (false == await Task.Run(() => ManagerState.Instance.IsPoweredOn)) { }
I'm not at all sure this is the best solution, but it does work, at least in my case. I guess I could move the logic to the handler and create a fancier singleton class for moving all the state back and forth, but that doesn't feel as good to me.
Related
Universal App with MVVMLight.
So I started wondering why all the SDK examples were done from code behind rather than using a solid Wrapper class.
So I wanted to write a reusable wrapper class. No luck. Even tried adding that wrapper to a ViewModel, still no luck.
Works fine from MainView.xaml.cs
IBandInfo[] pairedBands = BandClientManager.Instance.GetBandsAsync().Result;
if (pairedBands.Length > 0)
{
using (IBandClient bandClient = await BandClientManager.Instance.ConnectAsync(pairedBands[0]))
{
}
}
The moment I move to any kind of OOP or View Model, ConnectAsync will never return or throw exception. I have tried this 20 different ways, is the SDK broken? What Is happening? No message, no throw, just never returns.
If I throw in Code behind, wallah it works just fine and returns the client in 1/2 second.
I have spend 5-6 hours so far on this. I wanted to create a solid wrapper class for the SDK so I could call easy calls from Model and do things like StartListener(MicrosoftBandSensor sensorToActivate).
Any suggestions?
-- For Phil's comment
I was trying to create backing variables for both client and bandinfo which would be held in a class that the VM uses. I wrote my class as IDisposable so I could dispose of both when I was done with my wrapper. I may be using this wrong to be honest.
MicrosoftBand.MicrosoftBandClient = BandClientManager.Instance.ConnectAsync(pairedBands[0]).Result;
Is what I wanted to call making it a sync call since I wanted to make the calls to bandinfo and client in the constructor then hold both until the class was destroyed and just recall the vars when needed.
My VM has :
public BandInformation MicrosoftBand
{
get { return _microsoftBand; }
set { Set(() => MicrosoftBand, ref _microsoftBand, value); }
}
If they didn't pass the bandclient in the constructor I would use:
private async Task InitBand(IBandInfo bandInfo)
{
if (bandInfo == null)
{
var allBands = await BandClientManager.Instance.GetBandsAsync();
if (allBands.Length > 0)
{
bandInfo = allBands[0];
}
}
var bandClient = await BandClientManager.Instance.ConnectAsync(bandInfo);
MicrosoftBandInfo = bandInfo;
MicrosoftBandClient = bandClient;
if (MicrosoftBandClient == null)
{
AddErrorMessage("This sample app requires a Microsoft Band paired to your device.Also make sure that you have the latest firmware installed on your Band, as provided by the latest Microsoft Health app.");
}
}
This seems fine working with BandInfo. I get back a solid seeming to work object For the client I get "thread exited" and nothing else.
Note: I had it in a try catch throwaway version at one point and nothing threw n exception either.
I assume you can do this like you would any other IDisposable where you handle the disposing yourself.
I can reinstantiate the BandClient each time, just figured I needed to detach the events at some point, meaning I had to keep ahold of the bandclient. I could keep it until done and would add and remove events as I needed each time.
It's likely your blocking call to .Result within your VM constructor is what was causing the hang. IBandClientManager.ConnectAsync() may implicitly display UI (a Windows Runtime dialog asking the user to confirm that she wants to use that specific Bluetooth device). If you've blocked the UI thread when it attempts to display UI, you've now gotten yourself into a deadlock.
Calling Task.Result is almost never a good idea, much less doing so within a constructor where you have little idea on which thread the constructor is executing. If you're working with an async API (such as the Band SDK) then your best bet is to keep that interaction async as well. Instead, defer calling ConnectAsync() until you actually need to, and do so from an async method in your VM. (Deferring the connection is a good idea anyway because you want to minimize the time connected to the Band to preserve battery life.) Then call Dispose() as early as possible to close the Bluetooth connection.
So I went and looked at a bunch of examples. Finally I landed on the GravityHeroUAP demo on the MSDN site. https://msdn.microsoft.com/en-us/magazine/mt573717.aspx?f=255&MSPPError=-2147217396
I looked at his code and the source: https://github.com/kevinash/GravityHeroUWP
He was essentially doing what I wanted to do.
However, I noticed something Bizarre. In his viewmodel everything was static!
public static IBandInfo SelectedBand
{
get { return BandModel._selectedBand; }
set { BandModel._selectedBand = value; }
}
private static IBandClient _bandClient;
public static IBandClient BandClient
{
get { return _bandClient; }
set
{
_bandClient = value;
}
}
I ended up copying this pattern (though had to throw away my favorite MVVM lib in the process, though I am sure I can get it back).
My common pattern in my VM's:
public string ExceptionOnStart {
get { return _exceptionOnStart; }
set { Set(() => ExceptionOnStart, ref _exceptionOnStart, value); }
}
It seems to be working now!
That and I got data way too fast for the
await Windows.Storage.FileIO.AppendLinesAsync(dataFile, new List<string> { toWrite });
Thank you for the help Phil, it got me looking in the right direction!
Thank you very, very much. Spent WAY to long on this. Mark
We're trying to make a multipeer connection between two devices using MPC framework in libgdx game.
What generally we've done successfully:
Devices are connecting, session is establishing correctly.
After session is established nearBrowser and nearAdvertiser stop
doing their stuff.
Then we do transition to the game scene. In the new scene one device
can send a message to another.
DidReceiveData method from Session Delegate is called and there
we've got right messages for both devices.
After this we send to libgdx message for updating content (in main
gdx thread).
BUT after a while when some device received data it immediately crashes. Sometimes it happens on 10th receiving, sometimes after 200th. Crash appears only on the device that received message. It doesn't matter how long they are connected. Crash appears after all methods have done their work with data. So we don't know where exactly error happens.
// MCSession delegate method
public void didReceiveData(MCSession session, NSData data, MCPeerID peerID) {
//there we make userInfoData
//
DispatchQueue.getMainQueue().async(new Runnable() {
#Override
public void run() {
NSNotificationCenter.getDefaultCenter().postNotification(new NSString("didReceiveData"), null, userInfoData);
}
});
}
// Register observer in NSNotificationCenter
// NSNotificationCenter.getDefaultCenter().addObserver(this, Selector.register("updateDataWithNotification:"), new NSString("didReceiveData"), null);
// This method is called when device has received new data
#Method
private void updateDataWithNotification(NSNotification notification){
userInfoDict = notification.getUserInfo();
data = (NSData) userInfoDict.get(new NSString("data"));
strBytes = new String(data.getBytes());
// i'm not sure this Gdx.app.postRunnable is really needed
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
SBGlobalMessanger.getInstance().readBluetoothMessage(BluetoothData.RC_MESSAGE, strBytes);
}
});
}
The questions are:
Where is the bug? And how can we fix it?
The problem was in robovm plugin. In debug mode it made build that crushed. After making release build bug disappeared. The thing i have learned after working with robovm + libgdx is if you have strange bug just make a release build. It seems that this kind of bugs was eliminated with the last release of robovm 1.3 (i haven't try it out yet).
I'm wondering if anyone has figured out a way to properly handle timeouts in the JavaFX 8 (jdk 1.8.0_31) WebView. The problem is the following:
Consider you have an instance of WebView and you tell it to load a specific URL. Furthermore, you want to process the document once it's loaded, so you attach a listener to the stateProperty of the LoadWorker of the WebEngine powering the web view. However, a certain website times out during loading, which causes the stateProperty to transition into Worker.State.RUNNING and remain stuck there.
The web engine is then completely stuck. I want to implement a system that detects a timeout and cancels the load. To that end, I was thinking of adding a listener to the progressProperty and using some form of Timer. The idea is the following:
We start a load request on the web view. A timeout timer starts running immediately. On every progress update, the timer is reset. If the progress reaches 100%, the timer is invalidated and stopped. However, if the timer finishes (because there are no progress updates in a certain time frame we assume a time out), the load request is cancelled and an error is thrown.
Does anyone know the best way to implement this?
Kind regards
UPDATE
I've produced a code snippet with behavior described in the question. The only thing still troubling me is that I can't cancel the LoadWorker: calling LoadWorker#cancel hangs (the function never returns).
public class TimeOutWebEngine implements Runnable{
private final WebEngine engine = new WebEngine();
private ScheduledExecutorService exec;
private ScheduledFuture<?> future;
private long timeOutPeriod;
private TimeUnit timeOutTimeUnit;
public TimeOutWebEngine() {
engine.getLoadWorker().progressProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
if (future != null) future.cancel(false);
if (newValue.doubleValue() < 1.0) scheduleTimer();
else cleanUp();
});
}
public void load(String s, long timeOutPeriod, TimeUnit timeOutTimeUnit){
this.timeOutPeriod = timeOutPeriod;
this.timeOutTimeUnit = timeOutTimeUnit;
exec = Executors.newSingleThreadScheduledExecutor();
engine.load(s);
}
private void scheduleTimer(){
future = exec.schedule(TimeOutWebEngine.this, timeOutPeriod, timeOutTimeUnit);
}
private void cleanUp(){
future = null;
exec.shutdownNow();
}
#Override
public void run() {
System.err.println("TIMED OUT");
// This function call stalls...
// engine.getLoadWorker().cancel();
cleanUp();
}
}
I don't think that you can handle timeouts properly now. Looks at this method. As you can see it has hardcoded value for setReadTimeout method. Is it mean that SocketTimeoutException exception will be raised after one hour of loading site. And state will be changed to FAILED only after that event.
So, you have only one way now: try to hack this problem use Timers as you described above.
P.S.
Try to create issue in JavaFX issue tracker. May be anyone fixed it after 5 years...
I have the same problem and used a simple PauseTransition. Same behavior, not so complicated. =)
I am trying to figure out a way of timing out a connection to my streaming http source if it becomes unavailable. I do have checks in place which verify if I'm connected to the internet, either WIFI or Data. I did find some example using a Handler and I got to the point where it actually times out, but it doesn't stop the actual service which is in a different class. This causes a ANR in emulator. Any suggestions or different approaches on how this issue can be handled? Thank you for your time !
public Runnable mUpdateTimeTask = new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "Running Thread", Toast.LENGTH_LONG).show();
if (RadioService.isMusicActive()==false){
stopPlaying();
}
}
};
private void stopPlaying() {
buttonPlay.setEnabled(true);
buttonStopPlay.setEnabled(false);
stopService(new Intent(this, RadioService.class));//
}
if (v == buttonPlay) {
startPlaying();
mHandler.postDelayed(mUpdateTimeTask, 5000);}
After playing more with it and some minor modifications, this code worked the way I expected. Maybe it will help somebody else down the road.
I need to add GPS functionality to an existing Blackberry Application that I've written. I write a stand alone class called CurrentLocation, and include a method to set the various location variables I care about by using the blackberry GPS in conjunction with google's reverse geocoding webservices. Everything is working beautifully, that is, until I try to instantiate my new class in my main application.
No matter what I do, I get a LocationException! .getLocation() doesn't work!
It really confuses me, because if I instantiate my class in a test hello world app, it works just fine.
Are there limits to where you can instantiate classes? I've not encountered any with previous classes I've written. In this case, I'm instantiating my CurrentLocation class in a listener (so the user only makes the lengthy gps and web calls when they want to). I've tried instantiating it in screens, as well. I've tried just gutting the class and using the method call, but that doesn't work either.
Is there something I'm missing entirely here?
http://pastie.org/639545
There's a link to the class I'm making,
And here's the listener I"m trying to instantiate from. I'm in an event thread because I thought it might help (but I get the same exception whether or not I do this).
FieldChangeListener listenerGPS = new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
CurrentLocation loc = new CurrentLocation();
if (loc != null){
country = loc.getCountry();
city = loc.getCity();
state = loc.getState();
road = loc.getRoad();
zip = loc.getZip();
}
}
});
}
};
What am I missing here?
Okay, I got it. Apparently you can't call getLocation() in the eventThread (not just invokeLater, but any listener). So now what I'm doing is getting the coordinates in a thread outside of the event, and worrying about google separately.