Detecting connected/disconnected android wear device - wear-os

I am currently doing an android wear app that has 2 modes : connected (paired with the mobile phone) and disconnected. I looked at this thread (How to detect if android device is paired with android wear watch). I added listeners to detect when a node is found.
#Override
public void onPeerConnected(Node node) {
Log.d(TAG, "CONNECTED");
}
#Override
public void onPeerDisconnected(Node node) {
Log.d(TAG, "PEER DISCONNECTED");
}
There are the following cases :
- If the mobile app is launched, the wear app will detect a node and go into the connected mode.
- If the mobile app is killed, it sends a message before getting destroyed to the watch and the watch goes into disconnected mode.
- If the bluetooth connection is lost, the wear app goes into disconnected mode.
I am a bit lost on the way of doing this. How can I know if the correct node is paired? (the mobile that contains the app)?
EDIT :
Here is the code I use with the capabilities :
#Override
public void onCreate(){
mApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.build();
Toast.makeText(MainActivity.s_instance.getApplicationContext(), "mApiClient : " + mApiClient, Toast.LENGTH_SHORT).show();
if (mApiClient != null && !(mApiClient.isConnected() || mApiClient.isConnecting()))
mApiClient.connect();
setupNode();
Wearable.CapabilityApi.addCapabilityListener(
mApiClient,
capabilityListener,
CAPABILITY_NAME);
//Listener to check connection with the mobile (bluetooth enabled/disabled and device detected)
super.onCreate();
}
private void setupNode() {
new Thread(new Runnable() {
#Override
public void run() {
if (mApiClient != null && !(mApiClient.isConnected() || mApiClient.isConnecting()))
mApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
Wearable.CapabilityApi.getCapability(
mApiClient, CAPABILITY_NAME,
CapabilityApi.FILTER_REACHABLE).setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
#Override
public void onResult(CapabilityApi.GetCapabilityResult result) {
Toast.makeText(MainActivity.s_instance.getApplicationContext(), "TRYING TO GET NODE", Toast.LENGTH_SHORT).show();
if (result.getCapability() == null) {
Toast.makeText(MainActivity.s_instance.getApplicationContext(), "WE DETECTED NO CAPABILITY", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.s_instance.getApplicationContext(), "WE DETECTED A CAPABILITY : " + result.getCapability(), Toast.LENGTH_SHORT).show();
}
updateCapability(result.getCapability());
}
});
}
}).start();
}

Why not using CapabilitApi? If your app on the mobile provides a certain capability that you are interested in, you can use these apis to query for the nodes (connected and nearby) that provide that capability. Isn't that what you want?

Related

How do I get LocationManager.addNmeaListener() to work?

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.

On Android MainDisplayInfoChanged event doesn't trigger on RequestedOrientation

I'm trying to implement a mediaplayer in Xamarin.Forms, and to make a full screen function I've added a button which toggles the device between landscape and portrait. This works fine, but for some reason my android device doesn't trigger the DeviceDisplay.MainDisplayInfoChanged event until I actually flip the phone to match the new orientation.
So the app DOES change orientation, but doesn't fire the event until I physically flip my phone. This is happening on a physical device.
On iOS this works fine, so I'm a little confused.
My shared code on the video player page:
private void VideoControlView_ToggleFullScreen(object sender, EventArgs e)
{
if (_isFullScreen)
DependencyService.Get<IOrientationService>().SetOrientationToPortrait();
else
DependencyService.Get<IOrientationService>().SetOrientationToLandscape();
}
private async void DeviceDisplay_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if (AutoRotateDisabled)
return;
await DisplayVideoFullScreen(e.DisplayInfo.Orientation == DisplayOrientation.Landscape);
}
And my service to change the orientation for android:
public class OrientationService : IOrientationService
{
public void SetOrientationToLandscape()
{
((Activity)Forms.Context).RequestedOrientation = ScreenOrientation.Landscape;
}
public void SetOrientationToPortrait()
{
((Activity)Forms.Context).RequestedOrientation = ScreenOrientation.Portrait;
}
}
And for iOS just in case:
public class OrientationService : IOrientationService
{
private const string ORIENTATION_KEY = "orientation";
public void SetOrientationToLandscape()
{
SetOrientation(UIInterfaceOrientation.LandscapeRight);
}
public void SetOrientationToPortrait()
{
SetOrientation(UIInterfaceOrientation.Portrait);
}
public void SetOrientation(UIInterfaceOrientation orientation)
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)orientation), new NSString(ORIENTATION_KEY));
}
}
To recap:
When I trigger my ToggleFullScreen event, my android device corrently enters landscape mode, but it does NOT trigger MainDisplayInfoChanged.
I have tested this on two different physical android devices with the same issue.
Any help is appreciated, thanks!

Google Drive API implementation Xamarin Android

Our application should have the functionality to save Application files to Google Drive. Of course, using the local configured account.
From Android API i tried to figure out some clue. But android API with Xamarin implementation seems very tough for me.
I have installed Google Play Services- Drive from Xamarin Components but there are no examples listed from which we can refer the flow and functionality.
The basic steps (see the link below for full details):
Create GoogleApiClient with the Drive API and Scope
Try to connect (login) the GoogleApiClient
The first time you try to connect it will fail as the user has not selected a Google Account that should be used
Use StartResolutionForResult to handle this condition
When GoogleApiClient is connected
Request a Drive content (DriveContentsResult) to write the file contents to.
When the result is obtained, write data into the Drive content.
Set the metadata for the file
Create the Drive-based file with the Drive content
Note: This example assumes that you have Google Drive installed on your device/emulator and you have registered your app in Google's Developer API Console with the Google Drive API Enabled.
C# Example:
[Activity(Label = "DriveOpen", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity, GoogleApiClient.IConnectionCallbacks, IResultCallback, IDriveApiDriveContentsResult
{
const string TAG = "GDriveExample";
const int REQUEST_CODE_RESOLUTION = 3;
GoogleApiClient _googleApiClient;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate
{
if (_googleApiClient == null)
{
_googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(DriveClass.API)
.AddScope(DriveClass.ScopeFile)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(onConnectionFailed)
.Build();
}
if (!_googleApiClient.IsConnected)
_googleApiClient.Connect();
};
}
protected void onConnectionFailed(ConnectionResult result)
{
Log.Info(TAG, "GoogleApiClient connection failed: " + result);
if (!result.HasResolution)
{
GoogleApiAvailability.Instance.GetErrorDialog(this, result.ErrorCode, 0).Show();
return;
}
try
{
result.StartResolutionForResult(this, REQUEST_CODE_RESOLUTION);
}
catch (IntentSender.SendIntentException e)
{
Log.Error(TAG, "Exception while starting resolution activity", e);
}
}
public void OnConnected(Bundle connectionHint)
{
Log.Info(TAG, "Client connected.");
DriveClass.DriveApi.NewDriveContents(_googleApiClient).SetResultCallback(this);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_RESOLUTION)
{
switch (resultCode)
{
case Result.Ok:
_googleApiClient.Connect();
break;
case Result.Canceled:
Log.Error(TAG, "Unable to sign in, is app registered for Drive access in Google Dev Console?");
break;
case Result.FirstUser:
Log.Error(TAG, "Unable to sign in: RESULT_FIRST_USER");
break;
default:
Log.Error(TAG, "Should never be here: " + resultCode);
return;
}
}
}
void IResultCallback.OnResult(Java.Lang.Object result)
{
var contentResults = (result).JavaCast<IDriveApiDriveContentsResult>();
if (!contentResults.Status.IsSuccess) // handle the error
return;
Task.Run(() =>
{
var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream);
writer.Write("Stack Overflow");
writer.Close();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.SetTitle("New Text File")
.SetMimeType("text/plain")
.Build();
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFile(_googleApiClient, changeSet, contentResults.DriveContents);
});
}
public void OnConnectionSuspended(int cause)
{
throw new NotImplementedException();
}
public IDriveContents DriveContents
{
get
{
throw new NotImplementedException();
}
}
public Statuses Status
{
get
{
throw new NotImplementedException();
}
}
}
Ref: https://developers.google.com/drive/android/create-file

How to detect emergency call only in Xamarin?

Is there a way to detect if the phone, at the call try moment, can only make emergency calls (using Xamarin)?
This is something platform specific. Unfortunaelty I don't know any plugin for this, so you have to use the native API in Xamarin.
On Android it is the TelephonyManager as shown here: https://stackoverflow.com/a/14355706/1489968 It's Java, but can be easily translated to C#:
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var telMng = (TelephonyManager) GetSystemService(TelephonyService);
var myPhoneStateListener = new MyPhoneStateListener();
myPhoneStateListener.ServiceStateChanged += (s, e) => Console.WriteLine("State: {0}", e);
telMng.Listen(myPhoneStateListener, PhoneStateListenerFlags.ServiceState);
}
}
public class MyPhoneStateListener : PhoneStateListener
{
public event EventHandler<ServiceState> ServiceStateChanged;
public override void OnServiceStateChanged(ServiceState serviceState)
{
base.OnServiceStateChanged(serviceState);
ServiceStateChanged?.Invoke(this, serviceState);
}
}
On iOS: Not sure if that information is available, never have seen it exposed via the SDK... (maybe add iOS tag to this question, or ask an iOS only question, answer might be in ObjC/Swift but you can translate it)
On Android: The info you are looking for is contained within the ServiceState of the phone:
var callState = new ServiceState ();
switch (callState.State) {
case PhoneState.InService:
{
var uri = Android.Net.Uri.Parse ("tel:555-2368"); // Jim Rockford's number ;-)
var intent = new Intent (Intent.ActionDial, uri);
StartActivity (intent);
break;
}
case PhoneState.EmergencyOnly:
{
Toast.MakeText (this, "Emergency Calls Only", ToastLength.Long).Show();
break;
}
case PhoneState.OutOfService:
{
Toast.MakeText (this, "Out of Service", ToastLength.Long).Show();
break;
}
case PhoneState.PowerOff:
{
Toast.MakeText (this, "Cell/Modem Power Off", ToastLength.Long).Show();
break;
}
default:
{
Toast.MakeText (this, "Should never be shown on a real device", ToastLength.Long).Show();
break;
}
}
Ref: http://developer.android.com/reference/android/telephony/ServiceState.html
For testing on the emulator, you can set the state to denied via the adb shell:
voice denied
data denied
Ref: https://developer.android.com/tools/devices/emulator.html

Android Auto - Callback when user connects mobile device

In regards to Android auto how can i get a callback when user has plugged the device into a car ? I'd like to trigger an action to occur based on when the user actually connects to android auto, is it possible ?
You should be able to get USB connection broadcast. From there you will have to write your own logic to figure out Android Auto is in foreground.
(may need to introduce a slight delay in case android auto takes time to come up. Launcher seems to be part of google play service)
Here is how I did it in my services onCreate method (right form the example code):
IntentFilter filter = new IntentFilter(CarHelper.ACTION_MEDIA_STATUS);
mCarConnectionReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String connectionEvent = intent.getStringExtra(CarHelper.MEDIA_CONNECTION_STATUS);
mIsConnectedToCar = "media_connected".equals(connectionEvent);
LogHelper.i(TAG, "Connection event to Android Auto: ", connectionEvent,
" isConnectedToCar=", mIsConnectedToCar);
if(mIsConnectedToCar ) {
if(mService == null) {
bindMusicService();
if (mService != null) {
try {
initMediaMetaData();
toggleMediaPlaybackState(mService.isPlaying());
} catch (RemoteException e) {
Log.d(TAG, e.toString());
}
}
}
}
}
};
registerReceiver(mCarConnectionReceiver, filter);

Resources