Is there an event of a Android.Bluetooth class that is raised when the device is disconnected?
You have to set add a BluetoothGattCallback
public class MyGattCallback : BluetoothGattCallback
{
public override void OnConnectionStateChange(BluetoothGatt gatt, GattStatus status, ProfileState newState)
{
base.OnConnectionStateChange(gatt, status, newState);
if(newState == ProfileState.Disconnected)
{
// disconnected
}
}
}
And when you connect your device, you pass it:
BluetoothDevice device = ...;
var callback = new MyGattCallback();
device.ConnectGatt(Application.Context, false, callback);
Related
Here are the relevant parts of my code:-
public class MainActivity extends Activity
implements SensorEventListener, OnNmeaMessageListener {
private LocationManager m_locationManager;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ac = this;
m_locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
}
...
protected void onResume() {
...
if (m_locationManager != null) {
m_gpsSensor = new SensorView(this);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
m_locationManager.addNmeaListener(getMainExecutor(), this);
} else {
// no-op in later versions
m_locationManager.addNmeaListener(this);
}
m_gpsSensor.lineBreak("gps: ", "no messages yet");
} catch (Exception ignore) {
m_gpsSensor.lineBreak("gps: ", getString(R.string.permissiondenied));
}
topLayout.addView(m_gpsSensor);
}
}
#Override
public void onNmeaMessage(String message, long timestamp) {
long nanos = timestamp * 1000000;
if (nanos > m_gpsSensor.lastNanos + UPDATE_NANOS) {
m_gpsSensor.lastNanos = nanos;
m_gpsSensor.lineBreak("gps: ", message);
}
}
}
onNmeaMessage is never called (lastNanos is initialised to zero) and m_gpsSensor (a subclass of View) displays "no messages yet". My device does have GPS, and the app has permission to access it (otherwise it would display "Permission denied"), and GPS does work, because it can see satellites and get a fix with Satstat
I tried
addNmeaListener (OnNmeaMessageListener listener, Handler handler)
which doesn't work either. m_locationManager isn't null, because in that case it wouldn't display anything at all.
The device is a Samsung Galaxy S21 Ultra 5G running Android 12.
What am I doing wrong?
Apparently addNmeaListener doesn't start the GPS, and there doesn't seem to be an explicit call to do so. You have to call one of the requestLocationUpdates variants.
I am working on a Xamarin.Forms app that plays videos similar to a YouTube type app. I want the video to go full screen when the device rotates (like youtube does) but I also want the orientation to be locked in to portrait. Every post or tutorial I've found points to using custom renders for detecting orientation change to determine when the device rotates, but when orientation is locked those events do not fire.
Is there a way to detect device rotation without depending on orientation changing?
On iOS you would get device orientation with:
var orientation = UIDevice.CurrentDevice.Orientation;
On Android you need to ask the Window Manager:
var windowManager = ApplicationContext.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var orientation = windowManager.DefaultDisplay.Rotation;
You don't need a custom renderer, but you could suffice with a service you register in the service locator. This could looks something like.
In shared code:
public enum Orientation
{
None,
PortraitUp,
PortraitDown,
LandscapeLeft,
LandscapeRight
}
public interface IOrientationService
{
Orientation GetCurrentOrientation();
}
On Android:
[assembly: Dependency(typeof(AndroidOrientationService))]
public class AndroidOrientationService : IOrientationService
{
private readonly IWindowManager _windowManager;
public AndroidOrientationService()
{
_windowManager = ApplicationContext.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
}
public Orientation GetCurrentOrientation()
{
switch (_windowManager.DefaultDisplay.Rotation)
{
case SurfaceOrientation.Rotation0:
return Orientation.PortraitUp;
case SurfaceOrientation.Rotation180:
return Orientation.PortraitDown;
case SurfaceOrientation.Rotation90:
return Orientation.LandscapeLeft;
case SurfaceOrientation.Rotation270:
return Orientation.LandscapeRight;
default:
return Orientation.None;
}
}
}
Similarly on iOS:
[assembly: Dependency(typeof(IosOrientationService))]
public class IosOrientationService : IOrientationService
{
public Orientation GetCurrentOrientation()
{
switch (UIDevice.CurrentDevice.Orientation)
{
case UIDeviceOrientation.LandscapeLeft:
return Orientation.LandscapeLeft;
case UIDeviceOrientation.LandscapeRight:
return Orientation.LandscapeRight;
case UIDeviceOrientation.Portrait:
return Orientation.PortraitUp;
case UIDeviceOrientation.PortraitUpsideDown:
return Orientation.PortraitDown;
default:
return Orientation.None;
}
}
}
Then in your code you should be able to get the orientation like:
var orientationService = DependencyService.Get<IOrientationService>();
var orientation = orientationService.GetCurrentOrientation();
EDIT: detecting orientation changes
If you want to detect orientation changes on iOS you can do that by adding an observer for UIDeviceOrientation.
UIDevice.Notifications.ObserveOrientationDidChange(OnOrientationChanged);
Similarly on Android you can use SensorManager to listen to SensorType.Orientation changes. It has a bit more moving parts but looks something like follows.
You need to create a ISensorEventListener class:
class MyOrientationListner : Java.Lang.Object, ISensorEventListener
{
public event EventHandler OrientationChanged;
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
{
}
public void OnSensorChanged(SensorEvent e)
{
OrientationChanged?.Invoke(this, EventArgs.Empty);
}
}
Then you need to get the sensor manager from the current Context and start listening to orientation change events:
_sensorManager = context.GetSystemService(Context.SensorService).JavaCast<SensorManager>();
var sensor = _sensorManager.GetDefaultSensor(SensorType.Orientation);
var listener = new MyOrientationListner();
listener.OrientationChanged += OnOrientationChanged;
_sensorManager.RegisterListener(listener, sensor, SensorDelay.Normal);
private void OnOrientationChanged(object sender, EventArgs e)
{
OrientationChanged?.Invoke(this, GetCurrentOrientation());
}
Where OrientationChanged is a event in the IOrientationService:
event EventHandler<Orientation> OrientationChanged;
Then you can listen to that event where needed.
For iOS
In AppDelegate.cs override the below method
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application,UIWindow forWindow)
{
if (Xamarin.Forms.Application.Current == null || Xamarin.Forms.Application.Current.MainPage == null)
{
return UIInterfaceOrientationMask.Portrait;
}
var mainPage = Xamarin.Forms.Application.Current.MainPage;
if (mainPage is YourPage || (mainPage is NavigationPage &&
((NavigationPage)mainPage).CurrentPage is YourPage) || (mainPage.Navigation != null &&
mainPage.Navigation.ModalStack.LastOrDefault() is YourPage))
{
if (Configuration.IsFullScreen)
{
return UIInterfaceOrientationMask.Landscape;
}
}
return UIInterfaceOrientationMask.Portrait;
}
In a Dependency Service write the below method
public void ChangeLandscapeOrientation()
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.LandscapeLeft), new NSString("orientation"));
UINavigationController.AttemptRotationToDeviceOrientation();
}
Call the ChangeLandscapeOrientation method wherever you need it.
For Android
In a Dependency Service write the below method to change the orientation to Landscape
public void ChangeLandscapeOrientation()
{
var activity = (Activity)Xamarin.Forms.Forms.Context;
{
activity.RequestedOrientation = ScreenOrientation.Landscape;
var attrs = activity.Window.Attributes;
_originalFlags = attrs.Flags;
attrs.Flags |= Android.Views.WindowManagerFlags.Fullscreen;
activity.Window.Attributes = attrs;
}
}
Below code to change the orientation to Portrait
public void ChangePortraitOrientation()
{
var activity = (Activity)Xamarin.Forms.Forms.Context;
{
activity.RequestedOrientation = ScreenOrientation.Portrait;
var attrs = activity.Window.Attributes;
attrs.Flags = _originalFlags;
activity.Window.Attributes = attrs;
}
}
Hope it helps!
I'm currently trying to add some Bluetooth functionality to my app. I want to be able to change the Bluetooth Tethering on or off, as well as check its status.
I found the Java code on StackOverflow: How to check Bluetooth tethering status programmatically in Android
I have translated it into C#, but I don't seem to be able to get any result.
Regardless of the tethering setting, it always shows the toast with "Tethering:false", and the setBluetoothTethering doesn't change anything.
Any idea what I'm missing?
Here's my code:
[...]
try
{
Class classBluetoothPan = Class.ForName("android.bluetooth.BluetoothPan");
Method mBTPanConnect = classBluetoothPan.GetDeclaredMethod("connect", Class.FromType(typeof(BluetoothDevice)));
Constructor BTPanCtor = classBluetoothPan.GetDeclaredConstructor(Class.FromType(typeof(Context)), Class.FromType(typeof(IBluetoothProfileServiceListener)));
BTPanCtor.Accessible = true;
Java.Lang.Object BTSrvInstance = BTPanCtor.NewInstance(Activity, new BTPanServiceListener(Activity));
Method isTetheringOnMethod = classBluetoothPan.GetDeclaredMethod("isTetheringOn", null);
var isTetheringOn = isTetheringOnMethod.Invoke(BTSrvInstance);
Toast.MakeText(Activity, "Tethering:" + isTetheringOn, ToastLength.Short).Show();
Method setBluetoothTetheringMethod = classBluetoothPan.GetDeclaredMethod("setBluetoothTethering", new Class[1] { Class.FromType(typeof(bool)) });
setBluetoothTetheringMethod.Invoke(BTSrvInstance, true);
// tether = !tether;
}
catch (ClassNotFoundException e)
{
e.PrintStackTrace();
}
catch (Java.Lang.Exception e)
{
e.PrintStackTrace();
}
[...]
public class BTPanServiceListener : Java.Lang.Object, IBluetoothProfileServiceListener
{
private Activity _activity;
public BTPanServiceListener(Activity activity)
{
_activity = activity;
}
public void OnServiceConnected([GeneratedEnum] ProfileType profile, IBluetoothProfile proxy)
{
// throw new NotImplementedException();
}
public void OnServiceDisconnected([GeneratedEnum] ProfileType profile)
{
// throw new NotImplementedException();
}
}
I figured out how to enable Bluetooth tethering via setBluetoothTethering.
I wrote an entire blog about this
You can find the final code here
I assume that isTetheringOn works in the same way
I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});
I have one problem with publish/handle messages between 2 screens.
My scenario is:
Messenger screen, is it master screen, publish on chat screens, they are slave screens.
Messenger view model handle with messages from server.
Chat screen can publishes messages on messenger screen. And messanger view model send this message on server.
Messenger class look like this:
[Export("MessengerScreen", typeof(IMessengerViewModel))]
public class MessengerViewModel : Screen, IMessengerViewModel, IInitializable<Account>, IHandle<Rp>
{
// ...
[ImportingConstructor]
public MessengerViewModel(IPokecService service, IEventAggregator eventAgg)
{
_eventAgg = eventAgg;
_eventAgg.Subscribe(this);
}
//publish on slave screen
public void Publish(Rp rp)
{
_eventAgg.Publish(rp);
}
//handle msg from slave screen
public void Handle(Rp msg)
{
//send to server
}
}
Slave screen class look like this:
[Export("ChatScreen", typeof(IChatViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ChatViewModel : Screen, IInitializable<DetailData>, IHandle<Rp>
{
[ImportingConstructor]
public ChatViewModel(IEventAggregator eventAgg)
{
_eventAgg = eventAgg;
_eventAgg.Subscribe(this);
}
//publish only on messenger screen
public void Publish(Rp rp)
{
_eventAgg.Publish(rp);
}
//show message from published from messenger
public void Handle(Rp rp)
{
AddBlockToConversation(rp);
}
//if enter is pressed publish on messanger screen
public void SendRp(KeyEventArgs e)
{
if (e.Key == Key.Enter && !string.IsNullOrEmpty(RpText))
{
_yourRp.Time = String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now);
_yourRp.RpText = RpText;
AddBlockToConversation(_yourRp);
//publish on messanger screen
Publish(_yourRp);
}
}
}
My problems are:
First problem is:
I call method SendRp from class
ChatViewModel.
It calls method void Publish() in ChatViewModel,
then is call method void Handle() from class MessengerViewModel
and then call also method void
Handle() from ChatViewModel class.
I don’t want call method Handle() in ChatViewModel class. Why if I send message from ChatViewModel to MessengerViewModel is also called method Handle in ChatViewModel class?
My second problem is:
I would like publish from MessengerViewModel message on only certain slave screen.
MessgerVieModel have in queue messages: {msg1, msg2, msg3, ..., msgN}
I would like publish:
msg1 on slave screen #1.
msg2 on slave screen #2
...
msg3 on slave screen #3
MY SOLUTION:
I solved my problem with modification class EventAggregator.
Something like this:
Every my view model imlements this interface:
public interface IViewModelIdentity
{
string ScreenIdentity { get; set; }
}
And in Publish method in even aggregator class I have this:
public void Publish(Rp rp)
{
WeakReference[] toNotify;
lock (_subscribers)
toNotify = _subscribers.ToArray();
Execute.OnUIThread(() =>
{
Log.Info("Publishing {0}.", rp);
var dead = new List<WeakReference>();
foreach (var reference in toNotify)
{
var target = reference.Target as IHandle<Rp>;
//GET ID OF SCREEN
var screenId = reference.Target as IViewModelIdentity;
//!
if (target != null && screenId != null)
{
if (screenId.ScreenIdentity=="screen on which we want to send a message")
{
//PUBLISH ON SCREEN
target.Handle(rp);
}
}
else if (!reference.IsAlive)
dead.Add(reference);
}
if (dead.Count > 0)
{
lock (_subscribers)
dead.Apply(x => _subscribers.Remove(x));
}
});
}