How to check internet connection continuously in Xamarin.Android Native? - xamarin

My application is completely based on internet and it does not work without it, when the internet is not available or it is slow my application is getting stopped unfortunately.
I tried to implement try, catch but it is not helping me out as it is not throwing any exception, then I thought that I have to check the internet connectivity continuously till the app is running and stop any activity from performing and set a popup to connect to the internet.
I am able to get the popup whenever I call a method which has the following code inside it,
ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
NetworkInfo networkInfo = connectivityManager.ActiveNetworkInfo;
if (networkInfo == null)
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetTitle("Network");
alert.SetMessage("Please turn of your Wifi or Mobile Data From Settings");
alert.SetPositiveButton("OK", (senderAlert, args) => {
Intent intent = new Intent(Android.Provider.Settings.ActionSettings);
StartActivity(intent);
});
alert.SetNegativeButton("Cancel", (senderAlert, args) => {
Toast.MakeText(this, "Cancelled!", ToastLength.Short).Show();
Finish();
});
Dialog dialog = alert.Create();
dialog.Show();
But I am unable to get the connection checked continuously, So Can some one Please help me to complete get this functionality in my application.

You need to use a BroadcastReceiver to monitor network changes.
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "android.net.conn.CONNECTIVITY_CHANGE" })]
[Android.Runtime.Preserve(AllMembers = true)]
public class ConnectivityChangeBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action != ConnectivityManager.ConnectivityAction)
return;
//Check if is connected and raise a custom event or store
//the current in a static global variable
}
}
}
In Android 7.0, you need to remove IntentFilter from the class and register the receiver.
var receiver = new ConnectivityChangeBroadcastReceiver();
Application.Context.RegisterReceiver(receiver, new IntentFilter(ConnectivityManager.ConnectivityAction));
Another option is to use the ConnectivityPlugin https://github.com/jamesmontemagno/ConnectivityPlugin , which is easier to use.
CrossConnectivity.Current.ConnectivityChanged += HandleConnectivityChanged;
void HandleConnectivityChanged (object sender, ConnectivityChangedEventArgs e)
{
// You can check the network status in
//e.IsConnected
}
Note that The ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE permissions are required.

Related

How to update badge count while app is open?

I am using BottomNavigationBar plugin and it works fine.
My problem is I want to update badge count when notification is received while app is in running mode(i.e. open). I am using Setting Plugin for Xamarin and store count in that. On app initialize it display counts properly. But when my app is open I am changing value of Setting and immediately change the value of badge. How's that can be possible?
Where is several ways to do this.
First, if you are using Xamarin forms then you can use MessagingCenter. It is cross platform Event Bus implementation.
I used Xamarin.Form. Example for sending message
public MainPage()
{
InitializeComponent();
var count = 0;
_button.Clicked += (sender, args) =>
{
MessagingCenter.Send<MainPage, int>(this, "MyMessage", count++);
};
}
Example of reciever
public App()
{
InitializeComponent();
var mp = new MainPage();
MainPage = new MainPage();
MessagingCenter.Subscribe<MainPage, int>(this, "MyMessage", (sender, arg) => {
MainPage.DisplayAlert("MyMessage", $"Hit Count: {arg}", "ok");
});
}
There is more info and examples if you check Link
Second, add event to your settings class, and invoke it in your setter

IntentService in Xamarin PCL Solution

I am busy writing an application where the user needs to capture a lot of images and then they get packaged together with some text data and then they get uploaded to a local server. I want to implement the uploading on the Android platform through an Intent Service but I cannot find a good Xamarin Forms PCL example to show me how.
This is the method where I initialize the Intent to pass to the IntentService:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
Intent uploadIntent = new Intent();
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
And this is the IntentService itself.
[Service]
public class ServiceIntent : IntentService
{
public ServiceIntent() : base("ServiceIntent")
{
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
return base.OnStartCommand(intent, flags, startId);
}
public override void OnCreate()
{
base.OnCreate();
}
protected override void OnHandleIntent(Intent intent)
{
Uri serviceAddress = new Uri(intent.GetStringExtra("serviceAddress"));
Guid captureId = Guid.Parse(intent.GetStringExtra("captureId"));
CaptureEntity capture = new DatabaseConnection_Android().CreateConnection().Query<CaptureEntity>("SELECT * FROM [CaptureEntity]").Single(c => c.WorkflowId == captureId);
var images = new DatabaseConnection_Android().CreateConnection().Query<ImageEntity>("SELECT * FROM [ImageEntity]").Where(i => i.CaptureEntityId == capture.Id);
try
{
MultipartFormDataContent content = new MultipartFormDataContent();
StringContent strContent = new StringContent(
capture.XmlData,
Encoding.UTF8,
"text/xml");
IImageHandler handler = new ImageHandler_Droid();
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("workflow", capture.WorkflowId.ToString());
request.Method = HttpMethod.Post;
request.RequestUri = serviceAddress;
foreach (var image in images)
{
byte[] imageByte = handler.ReadAllBytes(image.ImagePath);
ByteArrayContent byteContent = new ByteArrayContent(imageByte);
byteContent.Headers.Add("Content-Type", "image/jpeg");
content.Add(byteContent, "file", image.ImageName);
}
content.Add(strContent, "text/xml");
request.Content = content;
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(180);
var response = client.SendAsync(
request,
HttpCompletionOption.ResponseContentRead).Result;
var readResponse = response.Content.ReadAsStringAsync().Result;
if (readResponse == "File uploaded.")
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Completed",
"Success");
else if (readResponse.Contains("An error has occurred."))
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Uploader",
String.Format(CultureInfo.InvariantCulture,
"Failed: {0}",
readResponse));
else
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Uploader",
String.Format(CultureInfo.InvariantCulture,
"Failed: {0}",
readResponse));
}
}
catch (WebException webExc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
webExc.Message));
}
catch (TimeoutException timeExc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
timeExc.Message));
}
catch (Exception exc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
exc.Message));
}
}
}
Can anyone tell me what I am doing wrong as I am getting the following error when I want to start the service:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
In your Intent declaration you need to tell the service you want to call
Something like this:
var uploadIntent = new Intent(this, typeof(ServiceIntent));
Note: this represents the Context.
Update:
As mentioned in the comments your interface implementation cannot derive from Activity class. In order to have access to the Context to be able to call the StartService method and also create your Intent you can make it in two ways:
Using the Xamarin.Forms.Forms.Context:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
var context = Xamarin.Forms.Forms.Context;
var uploadIntent = new Intent(context, typeof(ServiceIntent));
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
context.StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
If you are using latest versions of Xamarin.Forms this global context was deprecated and they suggest to you local context instead. You can still use it though but in future updates of XF your app might break.
using CurrentActivity plugin:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
var context = CrossCurrentActivity.Current.Activity;
var uploadIntent = new Intent(context, typeof(ServiceIntent));
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
context.StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
This plugin can be installed from nugget and the setup is very straight forward. Basically it gives you access to the current activity and you can use it as your context to call the IntentService
Hope this helps.-
Here is the IntentService.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
In Android, we usually use IntentService to do asynchronous operator. As we all know, thread is also used to do asynchronous operator. The difference between IntentService and Thread is IntentService is Service which belongs to Android Component. So, the priority of IntentService is higher than Thread.
For example, there is a ActivityA which has a IntentService, and there is a ActivityB which has a Thread, both IntentService and Thread are working, and both ActivityA and ActivityB are al background Activity. Now, if your phone's system doesn't have extra resources, your ActivityB will be killed firstly.
About the Exception:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
That means you should use android.content.Context to call the StartService method. In Android, there are three kinds of Context. Application, Activity and Service. So you can call the StartService method in these three classes directly. If you are not in these three classes, you need pass the Context to your class, and then use the Context to call StartService.
I added Activity for this class' inheritance.
If you do this, your class will be a Activity, and you need to register it in your manifiest, add layout for your class, and it should have the lifecycle, and etc. It will not be what you want to get class. In Android, Activity is a Component, not normal class, so you can't inherit it unless you want your class to be a Activity.
Demo:
I have made a demo for you,

App.OnResume error in Xamarin forms on Android and IOS devices

We are using xamarin forms. After an Android or IOS device resumes from background, we are making a REST call in .net that is being triggered by a timer. The first attempt on IOS returns a "The Descriptor is not a socket" error and the Android returns a "Connection refused" error. The same code works fine in Windows. Future attempts (every few seconds) in all 3 platforms work fine. Has anyone seen this and have a fix?
Code
//app on resume event
protected async override void OnResume()
{
// Handle when your app resumes
if (MainPage is RootPage)
{
RootPage mainPage = MainPage as RootPage;
if (mainPage.Detail is NavigationPage)
{
NavigationPage nvPage = mainPage.Detail as NavigationPage;
if(nvPage.CurrentPage is ThingsPage)
{
ThingsPage thPage = nvPage.CurrentPage as ThingsPage;
thPage.TurnOnTimer();
}
}
}
}
//code on the page
public void TurnOnTimer()
{
if (viewModel != null)
{
viewModel.ContinueTimer = true;
viewModel.StartAnotherTimer();
}
}
//code in view model
public async void StartAnotherTimer()
{
while (ContinueTimer)
{
try
{
DevicesUpdate devicesUpdate = await DataSource.GetDevices(LocationID, ControllerID, lastDevicesUpdateReceivedAt);
}
catch (Exception ex)
{
}
// Update the UI (because of async/await magic, this is still in the UI thread!)
if (ContinueTimer)
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
}
}
public static async Task<DevicesUpdate> GetDevices(Guid locationID, Guid controllerID, DateTime lastUpdateReceivedAt)
{
DevicesUpdate devicesUpdate = await GetLastUpdatedDevices(controllerID, lastUpdateReceivedAt);
}
//code in view model
public static async Task<DevicesUpdate> GetLastUpdatedDevices(Guid controllerID,
DateTime lastUpdate)
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
string url = string.Format("http://appname.azurewebsites.net/api/devices?controllerid={1}&lastUpdate={2}"
, Constants.WebServerURL, controllerID, lastUpdate);
System.Net.Http.HttpResponseMessage response = await client.GetAsync(new Uri(url));
string result = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
DevicesUpdate devices = JSONHelper.Deserialize<DevicesUpdate>(result);
return devices;
}
else
{
if (response.ReasonPhrase == "UserException")
{
throw new UserException(result);
}
else
{
//throw error because the response from rest api is not a success
throw new System.Net.Http.HttpRequestException(result);
}
}
}
You might have a few things happening here that's causing problems.
GetDevices doesn't return anything. (I hope you just left out the return for brevity sake)
You are never setting ContinueTimer to false.
What iOS version are you on? In later versions, you HAVE to use HTTPS or explicitly allow non-secure connections. This shouldn't be a problem because Azure has ssl.
If you plan on running this in the background, you need to register your app as a background process.
If you don't plan on running this in the background, you might have issues with previous attempts being ran (or still trying to execute, or just have failed) and then calling more.
What is the reason for calling the 3 second timer for the network calls? What if the call takes more than 3 seconds (then you are making duplicate calls even though the first might succeed).
If you want to make your network calls more robust, check out this Blog Post by Rob Gibbons about resilient network calls.
First thing I would do is remove it from the timer because it seems like the underlying sockets are having issues cross-thread.

Create a Share button on Windows Phone 8, 8.1 and 10 (Mobile)

How can I create a Share button (that share a defined mesage to another player contact) as the below image on Windows Phone 8, 8.1 and 10 (Mobile):
To create this script to share on Android Device I use the following code:
public class ShareScript : MonoBehaviour {
string subject = "Subject";
string body = "Body";
public void OnAndroidTextSharingClick()
{
StartCoroutine(ShareAndroidText());
}
IEnumerator ShareAndroidText()
{
yield return new WaitForEndOfFrame();
//execute the below lines if being run on a Android device
#if UNITY_ANDROID
//Reference of AndroidJavaClass class for intent
AndroidJavaClass intentClass = new AndroidJavaClass ("android.content.Intent");
//Reference of AndroidJavaObject class for intent
AndroidJavaObject intentObject = new AndroidJavaObject ("android.content.Intent");
//call setAction method of the Intent object created
intentObject.Call<AndroidJavaObject>("setAction", intentClass.GetStatic<string>("ACTION_SEND"));
//set the type of sharing that is happening
intentObject.Call<AndroidJavaObject>("setType", "text/plain");
//add data to be passed to the other activity i.e., the data to be sent
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_SUBJECT"), subject);
//intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TITLE"), "Text Sharing ");
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), body);
//get the current activity
AndroidJavaClass unity = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
//start the activity by sending the intent data
AndroidJavaObject jChooser = intentClass.CallStatic<AndroidJavaObject>("createChooser", intentObject, "Share Via");
currentActivity.Call("startActivity", jChooser);
#endif
}
}
Call DataTransferManager.ShowShareUI to show the sharing pane.
Handle the DataTransferManager.DataRequested event to provide the data when the user choses to share.
private void DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Share Text Example";
request.Data.Properties.Description = "An example of how to share text.";
request.Data.SetText("Hello World!");
}
See the Share data docs on MSDN for more info.
In Unity you can call these in an #if NETFX_CORE block so it runs only when using the Windows Runtime and not Mono. See Windows Store Apps: WinRT API in C# scripts. If you target Windows 10 then there are plug-ins at https://github.com/microsoft/unityplugins which include sharing. For earlier targets there are commercial plugins.

Fail to attach windows service with Skype4COM to Skype Client

I tried to create a windows service which will allow to interact with Skype Client.
I'm using SKYPE4COM.DLL lib.
When I create a simple console or win32 aplication all works ok (I have the Skype request for this application and it works well). But when I try to run this application as a service,
I have an error
Service cannot be started. System.Runtime.InteropServices.COMException (0x80040201): Wait timeout.
at SKYPE4COMLib.SkypeClass.Attach(Int32 Protocol, Boolean Wait)
at Commander.Commander.OnStart(String[] args)
at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
And I have no notification about process connecting to Skype.
Can you give me an advice how to attach service to Skype client or maybe I need to change my Skype settings?
I think it is not possible due to Windows User Id security restrictions. You have to run your application under the same user as Skype otherwise it won't be able to attach.
I had the same issue.
Resolved it by converting it to Windows Application and using it as System Tray App:
[STAThread]
static void Main()
{
Log.Info("starting app");
//facade that contains all code for my app
var facade = new MyAppFacade();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (ProcessIcon icon = new ProcessIcon(facade))
{
icon.Display();
Application.Run();
}
}
public class ProcessIcon : IDisposable
{
private readonly MyAppFacade facade;
private NotifyIcon ni;
public ProcessIcon(MyAppFacade facade)
{
this.facade = facade;
this.ni = new NotifyIcon();
}
public void Display()
{
ni.Icon = Resources.Resources.TrayIcon;
ni.Text = "Skype soccer";
ni.Visible = true;
// Attach a context menu.
ni.ContextMenuStrip = new ContextMenuStrip();
var start = new ToolStripMenuItem("Start");
start.Click += (sender, args) => facade.Start();
ni.ContextMenuStrip.Items.Add(start);
var stop = new ToolStripMenuItem("Stop");
stop.Click += (sender, args) => facade.Stop();
ni.ContextMenuStrip.Items.Add(stop);
var exit = new ToolStripMenuItem("Exit");
exit.Click += (sender, args) => Application.Exit();
ni.ContextMenuStrip.Items.Add(exit);
}
public void Dispose()
{
ni.Dispose();
}
}

Resources