How to monitor the changes in the Bluetooth Connectivity state changes from Xamarin.Forms - xamarin

Need to check the bluetooth connection to a remote device exists or got disconnected. Its basically a Forms which mainly targets Android and UWP.
I tried with the Dependency services and made the implementation in Android as below,
_[assembly: Xamarin.Forms.Dependency(typeof(BluetoothListenerActivity))]
namespace demotool.Droid
{
public class BluetoothListenerActivity : Activity,IBluetoothListener
{
public event EventHandler OnDeviceDisconnected;
public static BluetoothListenerActivity mySelf;
//string device;
public void start()
{
mySelf = this;
BluetoothStatusBroadCast mreceiver = new BluetoothStatusBroadCast();
IntentFilter mfilter = new IntentFilter(BluetoothDevice.ActionAclDisconnected);
Forms.Context.RegisterReceiver(mreceiver,mfilter);
}
public void receivedstatuschangd(string devicename,string state)
{
OnDeviceDisconnected(this, new DeviceDisconnectedEventArgs(name: devicename,status: state));
}
}
}_
BroadcastReceiver:
namespace Demo.Droid
{
[BroadcastReceiver]
class BluetoothStatusBroadCast : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
BluetoothDevice device =(BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
BluetoothListenerActivity.mySelf.receivedstatuschangd(device.Name, intent.Action);
}
}
}
Xamarin Forms Part:
_ protected override void OnStart()
{
IBluetoothListener bluetoothlistener = DependencyService.Get();
bluetoothlistener.start();
bluetoothlistener.OnDeviceDisconnected += Bluetoothlistener_OnDeviceDisconnected;
}
private void Bluetoothlistener_OnDeviceDisconnected(object sender, DeviceDisconnectedEventArgs e)
{
Page page1 = new Page();
page1.DisplayAlert(e.Name+ " " +e.Status, "Alert", "OK");
}_
The Intent Action that I have registered- BluetoothDevice.ActionAclDisconnected, is getting triggered once the Pairing is completed or a connection request is made, which I assume is not the actual Disconnection of the devices
Is there any common plugin which monitors the Bluetooth Connectivity Changes to a remote device. Or could you please tell me the actual Intent Action that I should listen for.
Thanks in Advance !

Related

Xamarin Android Share Link/Text via social media from custom renderer

I wan't to share a link via social media from custom renderer
public class CustomActions : ICustomActions
{
Context context = Android.App.Application.Context;
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
context.StartActivity(Intent.CreateChooser(sharingInt, "Share via"));
}
}
This error occur
Android.Util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
even when I added the below code I still get same error
sharingInt.AddFlags(ActivityFlags.NewTask);
The problem is that Intent.CreateChooser creates yet another Intent. What you want to do is to set the flag on this new intent:
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
var intent = Intent.CreateChooser(sharingInt, "Share via");
intent.AddFlags(ActivityFlags.NewTask);
context.StartActivity(intent);
}
Alternatively to avoid the need to do this, you could cache the MainActivity instance Xamarin.Forms uses:
public MainActivity
{
public static MainActivity Instance {get;private set;}
protected override void OnCreate(Bundle bundle)
{
Instance = this;
...
}
}
And then use the Instance as the Context in your code instead of the Application.Context

Cannot connect to google API client in Android Things

Here is my code
public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private String TAG = "app comm";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int code = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
if (code == ConnectionResult.SUCCESS) {
Log.d(TAG, "success ");
buildGoogleApiClient();
} else {
Log.d(TAG, "fail ");
}
}
private void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Nearby.CONNECTIONS_API).addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.d(TAG,"connected");
}
#Override
public void onConnectionSuspended(int i) {
Log.d(TAG,"suspended");
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.d(TAG,"failed");
}
}
I am new to this
I run this program in raspberry pi 3
I have checked and internet is working.
isGoogleServicesAvailable is returning true.
but none of the override methods called. I don't know what I am missing.
Here is my log
Connected to process 8191 on device google-iot_rpi3-192.168.1.2:5555
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/zygote: Late-enabling -Xcheck:jni
W/zygote: Using default instruction set features for ARM CPU variant (generic) using conservative defaults
I/InstantRun: starting instant run server: is main process
V/first log: first raspberry log message
D/app comm: success
D/vndksupport: Loading /vendor/lib/hw/android.hardware.graphics.mapper#2.0-impl.so from current namespace instead of sphal namespace.
Looking at your code snippet, you are not calling the connect method after building it, which is what actually starts the connection and gives a callback.

WearableDataListener Service doesn't invoke onDataChanged method

I am making a wear app which fetches data from database(which is on handheld) on launch of app home screen.
So when the homepage activity launches, It sends a message using Wearable.MessageApi.sendMessage function from the android wear to handheld. On the handheld I have the WearableListenerService which receives this message in onMessageReceived function and reads database. After reading database it sends putDatamapRequest to the wear.
Now on the wear side, I have another WearableListenerService. In this service, onDataChanged() function is never invoked. It runs at times, so far it ran for 2-3 times but otherwise it doesn't run. It's very random. Also once the data is received in Wear side, I set a static Arraylist, which I use to display data in Activity. But since the onDataChanged function is not always called, it gives empty array list.
Here is my AndroidManifest file of wear app where I declared the service:
<service
android:name="com.example.deals.DataListenerService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
Here is my code to send message from wear to handheld:
mGoogleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
if(!getConnectedNodesResult.getNodes().isEmpty())
{
node = getConnectedNodesResult.getNodes().get(0);
System.out.println("Connected: "+ node.getId());
Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), FETCH_ALL_DEALS, null).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e("Wear:", "ERROR: failed to send Message: " + sendMessageResult.getStatus());
}
else
System.out.println("success");
}
});
}
else
System.out.println("Wear not connected to Phone");
}
});
}
#Override
public void onConnectionSuspended(int i) {
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.v("Phone to wear connection failed", "onConnectionFailed: " + result);
}
})
.addApi(Wearable.API)
.build();
mGoogleApiClient.connect();
Here is my code for onMessageReceive on Handheld:
public void onMessageReceived(MessageEvent messageEvent) {
System.out.println("Message Received on Phone on launch of wear homepage");
if(messageEvent.getPath().equals(FETCH_ALL_DEALS)) {
sendSavedDeals(); //fetch from db and make a datamap object using PutDataRequest
System.out.println("Message Received on Phone on launch of wear homepage");
}
else {
System.out.println("Unable to recognise action for "+messageEvent.getPath());
}
}
Now on my wear side I have a WearableListenerService but it's onDataChanged method never gets called. Could you please help me with that.
onDataChanged() is only called when the data really did change. If you put the same data into the DataApi multiple times, the method is only called once until you write different data.
To trigger an action on the wear side, even when the data didn't change, send a message after putting data into the DataApi.
Data should be changed or deleted to get call-back to onDataChanged in WearabaleListenerService in your wear.
if you want make changes open APP-info from settings and clear-data then after force stop .
Finally launch your app in phone..but ensure that wearable listener service should be already started in your wear..
When onDataChanged() is not being called :
Firstly, ensure that the handheld activity is connecting to the API at the start :
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
otherwise, it fails silently.
If it still doesn't work, to facilitate the debug, add this override method and this class in the handheld activity to generate data every 5 seconds :
#Override
public void onResume() {
super.onResume();
mDataItemGeneratorFuture = mGeneratorExecutor.scheduleWithFixedDelay(
new DataItemGenerator(), 1, 5, TimeUnit.SECONDS
);
}
/** Generates a DataItem based on an incrementing count. */
private class DataItemGenerator implements Runnable {
private int count = 0;
#Override
public void run() {
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(COUNT_PATH);
putDataMapRequest.getDataMap().putInt(COUNT_KEY, count++);
PutDataRequest request = putDataMapRequest.asPutDataRequest();
Log.d("yourApp", "Generating DataItem: " + request);
if (!mGoogleApiClient.isConnected()) {
return;
}
Wearable.DataApi.putDataItem(mGoogleApiClient, request)
.setResultCallback(new ResultCallback() {
#Override
public void onResult(DataItemResult dataItemResult) {
if (!dataItemResult.getStatus().isSuccess()) {
Log.e("YourApp", "ERROR: failed to putDataItem, status code: "
+ dataItemResult.getStatus().getStatusCode());
}
}
});
}
}

Long Running Apps on Android Wear

The Android Wear ecosystem seems to be built around quick tasks which a user will interact with, and then close. This works great for most applications, but what about one which covers a long running task, and should not be automatically closed when the watch sleeps?
My specific case: Swing by Swing Golf GPS. The preferred operation would be to have the application remain active, and shown when the screen wakes due to user action. And the life-time of a single use will be between 2 to 4 hours.
What are some methods to go about keeping an application front and center on the Android Wear device for periods longer than a single use?
So, here is what I have come up with as a solution:
Build a notification with a PendingIntent to open the main Activity. Also pass it an intent for the delete action, so we know if the user has dismissed it.
public class SbsNotificationHelper {
private static final String NOTIFICATION_DELETED_INTENT = "sbs.notificationDeleted";
private static boolean _isNotificationActive = false;
/** Public static methods */
public static NotificationCompat.Builder buildRoundInProgressNotification(Context context) throws Throwable {
Intent viewIntent = new Intent(context, SbsRoundActivity.class);
PendingIntent viewPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
context.registerReceiver(_broadcastReceiver, new IntentFilter(NOTIFICATION_DELETED_INTENT));
_isNotificationActive = true;
Intent deleteIntent = new Intent(NOTIFICATION_DELETED_INTENT);
PendingIntent deletePendintIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.circle_button, "", viewPendingIntent).build();
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.bottom_bg);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.iphone_57x57)
.setLargeIcon(bitmap)
.setContentTitle("Golf GPS")
.setContentText("Swing by Swing")
.addAction(action)
.setDeleteIntent(deletePendintIntent)
.extend(new NotificationCompat.WearableExtender()
.setContentAction(0));
return notificationBuilder;
}
public static boolean isNotificationActive() {
return _isNotificationActive;
}
/** BroadcastReceiver */
private static final BroadcastReceiver _broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
_isNotificationActive = false;
}
};
}
Use onStop() as opposed to onPause() to issue the notification. This way, if you have multiple activities in your app, you can present them (only causing onPause() of the main Activity).
#Override
protected void onStop() {
super.onStop();
int notificationId = 001;
NotificationCompat.Builder notificationBuilder = SbsNotificationHelper.buildRoundInProgressNotification(context);
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
notificationManagerCompat.notify(notificationId, notificationBuilder.build());
}
Also use the notification inside of your WearableListenerService if you communicate with an app on the handheld. Thus a notification can be popped and easily accessed when your app is opened.
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
try {
if (SEND_MESSAGE_PATH.equalsIgnoreCase(messageEvent.getPath())) {
if (!SbsNotificationHelper.isNotificationActive()) {
int notificationId = 001;
NotificationCompat.Builder notificationBuilder = SbsNotificationHelper.buildRoundInProgressNotification(sbsApplication);
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
notificationManagerCompat.notify(notificationId, notificationBuilder.build());
}
}
}
catch (Throwable throwable) {
//Handle errors
}
}
the OnPause() method is called whenever the device is put to sleep or dialogue appears over the application. One or two activities can be done here, but they should be kept reasonably lightweight to prevent elongated user wait times.
I've had no problem doing a "extends Service" app on Wear device that works perfectly fine.
Basically: In your Wear app - decouple your GUI and app logic. Keep the app logic inside the service. I keep a class object that holds all the GUI data and pull it static from the service when Activity starts.
You could extend wearable service, but I use just the generic service as the center of my app and that worked perfectly fine (app runs for days without trouble).

How to run code on Application opens in Android?

My Android app need the user to create an account to be able to use the app. The account info is stored in SQLite database. When the application starts I check if the user has an account, if not I show a sign up activity for the user.
Now I get reports from users that they sometimes comes to the sign up activity even if they've already created an account. This happens when they've closed the application and reopen it again.
This is the code I'm using and I need to figure out what the problem might be:
//MyApplication.java
public class MyApplication extends Application {
private DataBaseUtility dbu;
public boolean hasAccount;
#Override
public void onCreate() {
super.onCreate();
//Init sqlite database
this.dbu = new DataBaseUtility(this);
//This loads the account data from the database and returns true if the user has already created an account
this.hasAccount = loadAccount();
}
public boolean loadAccount() {
boolean loadedData = false;
String query = "SELECT data FROM tblaccount WHERE tblaccount.deleted=0";
Cursor cursor = this.dbu.getCursor(query);
if (cursor != null) {
while (cursor.moveToNext()) {
loadedData = true;
}
cursor.close();
}
return loadedData;
}
}
//MainActivity.java
public class MainActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyApplication application = (MyApplication)getApplication();
if (!application.hasAccount) {
//Take the user to the sign up activity
}
}
My idea is that maybe sometimes MainActivity.onCreate() runs before MyApplication.onCreate(). Can that be the case?
In application's onCreate, you are checking if the user has an account and setting a boolean.
You are checking in the MainActivity's onCreate if the user has an account through the application's boolean.
application's onCreate() executing before MainActivity's onCreate() is always the case! It is impossible for a different execution path to occur and since application's onCreate() does not have a Runnable it is a 100% garantuee.
Please make sure you're DataBaseUtility does not have any Runnables.
Anyway STILL there are several ways to reproduce the error! I will not state these now but you can know them when you see:
SOLUTION
MainActivity You have forgotten to update application.hasAccount upon successfull sign up~
public class MainActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyApplication application = (MyApplication)getApplication();
if (!application.hasAccount) {
//Take the user to the sign up activity
//if(successful) application.hasAccount = true
}
}
To avoid database exceptions
I use this:
REMARK It would be much better to use more strong persistent status saving for the database -i.e. SharedPreferences
boolean isOpened = false;
//When I need to open
if(!isOpened){
//open
isOpened = true;
}
//When I need to close
if(isOpened){
//close
isOpened = false;
}
onDestroy() { //every onDestroy
if(isOpened){
//close
}
}

Resources