foreground service never stop xamarin android - c#

i have xamarin android application that read from clipboard and write to it. it uses foreground services if the user hit the button. the problem is when the user hit the button again the services disappear from running services but still doing it's job (editing copied text). how can i completely stop it from working?
foreground.cs:
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Clipboard.ClipboardContentChanged += async (o, e) =>
{
var text = await Clipboard.GetTextAsync();
Toast.MakeText(this, text, ToastLength.Long).Show();
if (text.Contains("#"))
{
await Clipboard.SetTextAsync(text.Replace("#", ""));
}
};
Notification notif = ReturnNotif();
StartForeground(1, notif);
return StartCommandResult.NotSticky;
}
public override void OnDestroy()
{
base.OnDestroy();
}
public override void OnCreate()
{
base.OnCreate();
}
public override bool StopService(Intent name)
{
StopForeground(true);
StopSelf();
return base.StopService(name);
}
MainActivity.cs:
if (id == Resource.Id.myService)
{
if (count != 1)
{
count = 1;
var intent = new Intent(this, typeof(foreground));
intent.SetAction("No");
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
StartForegroundService(intent);
}
else
{
StartService(intent);
}
}
else
{
var intent = new Intent(this,typeof(foreground));
StopService(intent);
Toast.MakeText(this, "Service Stopped", ToastLength.Long).Show();
count = 0;
}
}
what i am doing wrong ?
Edit :
Service will completely stops if the app is removed from recently used apps.

In your MyForegroundService.cs. Just add StopForeground(true) in OnDestroy() method like following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace ForegroundServiceDemo
{
[Service]
class MyForegroundService : Service
{
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
CreateNotificationChannel();
string messageBody = "service starting";
// / Create an Intent for the activity you want to start
Intent resultIntent = new Intent(this,typeof(Activity1));
// Create the TaskStackBuilder and add the intent, which inflates the back stack
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddNextIntentWithParentStack(resultIntent);
// Get the PendingIntent containing the entire back stack
PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(0, PendingIntentFlags.UpdateCurrent);
var notification = new Notification.Builder(this, "10111")
.SetContentIntent(resultPendingIntent)
.SetContentTitle("Foreground")
.SetContentText(messageBody)
.SetSmallIcon(Resource.Drawable.main)
.SetOngoing(true)
.Build();
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
//do you work
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
base.OnDestroy();
StopForeground(true);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return;
}
var channelName = Resources.GetString(Resource.String.channel_name);
var channelDescription = GetString(Resource.String.channel_description);
var channel = new NotificationChannel("10111", channelName, NotificationImportance.Default)
{
Description = channelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
}
When you want to stop it. Just call following code.
Android.App.Application.Context.StopService(intent);
Here is my code in the Activity.
public class MainActivity : AppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
Button button1 = FindViewById<Button>(Resource.Id.button1);
Button button2 = FindViewById<Button>(Resource.Id.button2);
button2.Click += Button2_Click;
button1.Click += Button1_Click;
}
Intent intent;
private void Button2_Click(object sender, System.EventArgs e)
{
// stop foreground service.
Android.App.Application.Context.StopService(intent);
}
private void Button1_Click(object sender, System.EventArgs e)
{
intent = new Intent(Android.App.Application.Context, typeof(MyForegroundService));
// start foreground service.
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
StartForegroundService(intent);
}
}
Here is running GIF.
====Update====
the expected behavior is : when service stopped user can copy and paste normally without the service interference
You can use following way to achieve that.Use Clipboard.ClipboardContentChanged += Clipboard_ClipboardContentChanged; to execute the behavior, use Clipboard.ClipboardContentChanged -= Clipboard_ClipboardContentChanged; to disable the behavior in the OnDistory method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Essentials;
namespace ForegroundServiceDemo
{
[Service]
class MyForegroundService : Service
{
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
CreateNotificationChannel();
string messageBody = "service starting";
Clipboard.ClipboardContentChanged += Clipboard_ClipboardContentChanged;
// / Create an Intent for the activity you want to start
Intent resultIntent = new Intent(this,typeof(Activity1));
// Create the TaskStackBuilder and add the intent, which inflates the back stack
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddNextIntentWithParentStack(resultIntent);
// Get the PendingIntent containing the entire back stack
PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(0, PendingIntentFlags.UpdateCurrent);
var notification = new Notification.Builder(this, "10111")
.SetContentIntent(resultPendingIntent)
.SetContentTitle("Foreground")
.SetContentText(messageBody)
.SetSmallIcon(Resource.Drawable.main)
.SetOngoing(true)
.Build();
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
//do you work
return StartCommandResult.Sticky;
}
private async void Clipboard_ClipboardContentChanged(object sender, EventArgs e)
{
//throw new NotImplementedException();
var text = await Clipboard.GetTextAsync();
Toast.MakeText(this, text, ToastLength.Long).Show();
if (text.Contains("#"))
{
await Clipboard.SetTextAsync(text.Replace("#", ""));
}
}
public override void OnDestroy()
{
base.OnDestroy();
Clipboard.ClipboardContentChanged -= Clipboard_ClipboardContentChanged;
StopForeground(true);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return;
}
var channelName = Resources.GetString(Resource.String.channel_name);
var channelDescription = GetString(Resource.String.channel_description);
var channel = new NotificationChannel("10111", channelName, NotificationImportance.Default)
{
Description = channelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
}
Here is run GIF.

To prevent this issues, add a "myServiceIsRunning" bool value in the Properties dictionary when you start your foreground method like this:
Application.Current.Properties["myServiceIsRunning"] = true
With a get function or whatever you want, before start the foreground method, verify if the key "myServiceIsRunning" exist, and if it exist, how is his status:
public bool getRunningStateMyService(){
bool myServiceIsRunningValue;
if (Application.Current.Properties.ContainsKey("myServiceIsRunning"))
{
myServiceIsRunningValue = Convert.ToBoolean(Application.Current.Properties["myServiceIsRunning"]);
}
else
{
myServiceIsRunningValue = false;
}
return locationServiceIsRunning;
}
After you get the value, with a simple if{}/else{}, you can choose to start a new instance or not if you're service is running or not. Or, in you're case just prevent the foreground method continue to run.
Don't forget to set your value to false when you destroy the foreground method.
the Properties dictionary can store data trought closing app to. you can have more infos here:
https://learn.microsoft.com/fr-fr/xamarin/xamarin-forms/app-fundamentals/application-class#Properties_Dictionary

Related

Running a periodic service/ task/ function in mobile development on an android device

I am trying to collect location data from my own android phone for a project, but so far I'm not successful in letting functions run in the background.
I have tried the following:
Run repeated functions OnSleep()
This process automatically stopped after a few minutes
Use a Foreground Service with a repeated function inside of it
This is what I attempted with my Foreground Service:
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
MainActivity.cs
[Activity(Label = "LocationApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Activity ActivityCurrent { get; private set;}
protected override void OnCreate(Bundle savedInstanceState)
{
ActivityCurrent = this;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
RequestPermissions();
}
private async void RequestPermissions()
{
await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
await CrossPermissions.Current.RequestPermissionAsync<LocationAlwaysPermission>();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
TracingServices.cs
[assembly:Xamarin.Forms.Dependency(typeof(LocationApp.Droid.TracingServices))]
namespace LocationApp.Droid
{
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class TracingServices : Service,ITracingServices
{
private List<Timer> Timerlist = new List<Timer>();
private double initialLon;
private double initialLat;
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if(intent.Action=="START_SERVICE")
{
Console.WriteLine("Started service");
RegisterNotification();
TrackLocation();
}
else if(intent.Action=="STOP_SERVICE")
{
Console.WriteLine("Stopping service");
StopForeground(true);
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(TracingServices));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
stopIntent.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(stopIntent);
// stop the timer
foreach (Timer timer in Timerlist)
{
timer.Stop();
}
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("Servicechannel", "Service demo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "Servicechannel")
.SetContentTitle("Testing service")
.SetOngoing(true)
.Build();
StartForeground(100, notification);
}
private async void TrackLocation()
{
// track initial coordinates
Location result = await Geolocation.GetLocationAsync();
initialLat = result.Latitude;
initialLon = result.Longitude;
// set timer to launch every 3 minutes
Timer timer = new Timer(3 * 60 * 1000);
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, initialLat, initialLon);
timer.AutoReset = true;
timer.Enabled = true;
Timerlist.Add(timer);
}
private static async void OnTimedEvent(object source, ElapsedEventArgs e, double lat, double lon)
{
Location result = await Geolocation.GetLocationAsync();
Console.WriteLine($"Final stuff: {result.Latitude},{result.Longitude},{result.Timestamp.DateTime:dd/MM},{result.Timestamp.DateTime:HH:mm}");
await App.Database.SaveLocationAsync(new LocationData
{
Latitude = result.Latitude - lat,
Longitude = result.Longitude - lon,
Day = result.Timestamp.DateTime.ToString("dd/MM"),
Time = result.Timestamp.DateTime.ToString("HH:mm")
});
}
}
}
MainPage.xaml.cs function
async private void ToggleTracking(object send, EventArgs args)
{
var status = await SecureStorage.GetAsync("Tracking");
// if the status starts as false, set it to true, update the button text and execute the Foreground service
if (status != "true")
{
await SecureStorage.SetAsync("Tracking", "true");
ToggleButton.Text = "Stop tracking";
DependencyService.Get<ITracingServices>().Start();
}
// if the status starts as true, set it to false, update the button text and end the Foreground service
else
{
await SecureStorage.SetAsync("Tracking", "false");
ToggleButton.Text = "Start tracking";
DependencyService.Get<ITracingServices>().Stop();
}
}
I am sure the ToggleTracking function is called, as my device displays the Foreground Service as being active, but the function isn't being executed.
Is it possible to edit the Foreground Service so that it still runs the function with an interval or is there a different option that can run functions at an interval in the background?
Changing the order of operations in the TracingServices class has caused the application to be called, though not with regular interval. Sometimes it executes, sometimes it doesn't for a few intervals. This is how I changed the order:
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class TracingServices : Service,ITracingServices
{
private List<Timer> Timerlist = new List<Timer>();
private double initialLon;
private double initialLat;
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if(intent.Action=="START_SERVICE")
{
Console.WriteLine("Started service");
RegisterNotification();
}
else if(intent.Action=="STOP_SERVICE")
{
Console.WriteLine("Stopping service");
StopForeground(true);
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(TracingServices));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
stopIntent.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(stopIntent);
// stop the timer
foreach (Timer timer in Timerlist)
{
timer.Stop();
}
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("Servicechannel", "Service demo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "Servicechannel")
.SetContentTitle("Testing service")
.SetOngoing(true)
.Build();
StartForeground(100, notification);
TrackLocation();
}
private async void TrackLocation()
{
Console.WriteLine("Tracking called");
// track initial coordinates
Location result = await Geolocation.GetLocationAsync();
initialLat = result.Latitude;
initialLon = result.Longitude;
// set timer to launch every 3 minutes
Timer timer = new Timer(3 * 60 * 1000);
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, initialLat, initialLon);
timer.AutoReset = true;
timer.Enabled = true;
Console.WriteLine("Timer set");
Timerlist.Add(timer);
}
private static async void OnTimedEvent(object source, ElapsedEventArgs e, double lat, double lon)
{
Location result = await Geolocation.GetLocationAsync();
Console.WriteLine($"Final stuff: {result.Latitude},{result.Longitude},{result.Timestamp.DateTime:dd/MM},{result.Timestamp.DateTime:HH:mm}");
await App.Database.SaveLocationAsync(new LocationData
{
Latitude = result.Latitude - lat,
Longitude = result.Longitude - lon,
Day = result.Timestamp.DateTime.ToString("dd/MM"),
Time = result.Timestamp.DateTime.ToString("HH:mm")
});
}
}
If there is a way for the function to fire every time at an interval, I'd love to hear it.

How to set ,reset a timer using CountDownTimer class in Xamarin.Android

I made a simple Countdowntimer app for another project , I tried my best to run this simple timer to start,pause and reset the timer.My first problem was the fact that,
Since a MainActivity cannot inherit multiple classes I had to make a new one just so I can Inherit the abstract class CountDownTimer,Secondly I do not know completely that I was able to call the CountDownTimer constructor from the abstract class because I cannot intialize an abstract class only call it form within the subclass constructor .Also , I dont understand how do I wire it up with my UI.I'm completely lost in this, Can anyone help me on this?? .I have also published the code on github
GitHub : https://github.com/owais19m/TimerApp_Test
Thanks ,
Blockquote
using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Widget;
using Android.Content;
using Android.Telephony;
using Android.Provider;
using Android.Util;
using Java.Lang;
using System.Text.RegularExpressions;
using Xamarin.Essentials;
using System;
using Android;
using Android.Support.V4.Content;
using Android.Content.PM;
namespace TimerApp_Test
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
private static System.Boolean mTimerRunning;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//------------ Views -------------
Button ResetBtn = FindViewById<Button>(Resource.Id.ResetButton);
Button StartBtn = FindViewById<Button>(Resource.Id.StartButton);
TextView TimerText = FindViewById<TextView>(Resource.Id.CountdownText);
//--------------------------------
StartBtn.Click += (s, e) =>
{
if (mTimerRunning)
{
pauseTimer();
}
else
{
startTimer();
}
};
ResetBtn.Click += (s, e) =>
{
resetTimer();
};
}
public void startTimer()
{
CountDown1 Coutdwn = new CountDown1(6000,1000);
Coutdwn.OnTick(1000);
Coutdwn.Start();
mTimerRunning = true;
}
public void resetTimer()
{
}
public void pauseTimer()
{
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class CountDown1 : CountDownTimer
{
public static long Start_Time_Milliseconds = 600000;
public static CountDownTimer mcountDownTimer;
public static long mTimeLeftInMillis = Start_Time_Milliseconds;
public CountDown1(long mTimeLeftInMilli, long countDownInterva) : base(mTimeLeftInMilli,countDownInterva)
{
}
public override void OnFinish() { }
public override void OnTick(long millisUntilFinished)
{
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
public string updateCountDownText()
{
int minutes = (int)mTimeLeftInMillis / 1000 / 60;
int seconds = (int)mTimeLeftInMillis / 1000 % 60;
string timeLeftFormatted = string.Format("%02d:%02d", minutes, seconds);
return timeLeftFormatted;
}
public void Cancellation()
{
mcountDownTimer.Cancel();
}
}
}
Blockquote
You could pass the TextView which you would display the time as the parameter into your custom CountDownTimer like below:
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
CountDown1 Coutdwn;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//------------ Views -------------
Button ResetBtn = FindViewById<Button>(Resource.Id.ResetButton);
Button StartBtn = FindViewById<Button>(Resource.Id.StartButton);
TextView TimerText = FindViewById<TextView>(Resource.Id.CountdownText);
Coutdwn = new CountDown1(60000, 1000, TimerText);
//--------------------------------
StartBtn.Click += (s, e) =>
{
startTimer();
};
ResetBtn.Click += (s, e) =>
{
resetTimer();
};
}
public void startTimer()
{
Coutdwn.Start();
}
public void resetTimer()
{
myTimer.Cancel();
myTimer.Start();
}
public void pauseTimer()
{
myTimer.Cancel();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class CountDown1 : CountDownTimer
{
TextView timeTextView;
public CountDown1(long mTimeLeftInMilli, long countDownInterva,TextView textView) : base(mTimeLeftInMilli,countDownInterva)
{
timeTextView = textView;
}
public override void OnFinish() { }
public override void OnTick(long millisUntilFinished)
{
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
public void updateCountDownText()
{
int minutes = (int)mTimeLeftInMillis / 1000 / 60;
int seconds = (int)mTimeLeftInMillis / 1000 % 60;
string timeLeftFormatted = string.Format("%02d:%02d", minutes, seconds);
timeTextView.Text = timeLeftFormatted;
}
}
Thanks, This worked great ,just regarding this, I coded a simple notification program when my timer stops in OnFinished() method in the CountDown1 class ,I get a "Java.Lang.IllegalStateException: 'System services not available to Activities before onCreate()' " error ,is there a work around for this ?
public NotificationCompat.Builder builder;
MainActivity activity = new MainActivity();
NotificationManagerCompat notificationManager;
public override void OnFinish()
{
Toast.MakeText(Application.Context ,"Finished",ToastLength.Short).Show();
builder = new NotificationCompat.Builder(this.activity, CHANNEL_ID).SetAutoCancel(true)
.SetContentTitle("CountDownTimer !!")
.SetSmallIcon(Resource.Drawable.abc_ic_star_black_48dp)
.SetContentText($" Stopped"); // display.
notificationManager = NotificationManagerCompat.From(this.activity); Error // Java.Lang.IllegalStateException: 'System services not available to Activities...
notificationManager.Notify(NOTIFICATION_ID, builder.Build());
}

xamarin android webview file upload download does not work . please i am comfortable with c#

i have created a mobile app for my website .the webview does not handle upload and download .so i followed the link Uploading files using a webview in Xamarin Android App and included the code from this link but does not handle upload and download. please guide. the storeactivity.cs code here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Net;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
namespace smartapp
{
[Activity(Label = "StoreActivity")]
public class StoreActivity : Activity
{
static ProgressBar progressBar;
WebView webview;
IValueCallback mUploadMessage;
private static int FILECHOOSER_RESULTCODE = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.StoresView);
webview = FindViewById<WebView>(Resource.Id.StoreView);
// show progress bar
progressBar = FindViewById<ProgressBar>(Resource.Id.progressBar);
// chrome client to upload files from webview
var chrome = new FileChooserWebChromeClient((uploadMsg, acceptType, capture) => {
mUploadMessage = uploadMsg;
var i = new Intent(Intent.ActionGetContent);
i.AddCategory(Intent.CategoryOpenable);
i.SetType("image/*");
StartActivityForResult(Intent.CreateChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
});
webview.LoadUrl("https://smartbook.pk/Stores/index");
webview.SetWebViewClient(new WebViewListener());
// download files from webview
webview.SetDownloadListener(new MyDownloadListerner(this));
webview.SetWebChromeClient(chrome);
webview.Settings.JavaScriptEnabled = true;
}
//
class MyDownloadListerner : Java.Lang.Object, IDownloadListener
{
Context cont;
public MyDownloadListerner(Context context)
{
cont = context;
}
public void OnDownloadStart(string url, string userAgent, string contentDisposition, string mimetype, long contentLength)
{
Android.Net.Uri uri = Android.Net.Uri.Parse(url);
Intent intent = new Intent(Intent.ActionView, uri);
cont.StartActivity(intent);
}
}
//
//
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage)
return;
Java.Lang.Object result = intent == null || resultCode != Result.Ok
? null
: intent.Data;
mUploadMessage.OnReceiveValue(result);
mUploadMessage = null;
}
}
// file chooser code
partial class FileChooserWebChromeClient : WebChromeClient
{
Action<IValueCallback, Java.Lang.String, Java.Lang.String> callback;
public FileChooserWebChromeClient(Action<IValueCallback, Java.Lang.String, Java.Lang.String> callback)
{
this.callback = callback;
}
//For Android 4.1
[Java.Interop.Export]
public void openFileChooser(IValueCallback uploadMsg, Java.Lang.String acceptType, Java.Lang.String capture)
{
callback(uploadMsg, acceptType, capture);
}
}
// webview listener code here
public class WebViewListener : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return true;
}
public override void OnPageStarted(WebView view, string url, Android.Graphics.Bitmap favicon)
{
progressBar.Progress = view.Progress;
}
public override void OnLoadResource(WebView view, string url)
{
progressBar.Progress = view.Progress;
}
public override void OnPageFinished(WebView view, string url)
{
progressBar.Progress = 0;
}
}
public override bool OnKeyDown(Android.Views.Keycode keyCode, Android.Views.KeyEvent e)
{
if (keyCode == Keycode.Back && webview.CanGoBack())
{
webview.GoBack();
return true;
}
return base.OnKeyDown(keyCode, e);
}
}
}
This is the Working Code of File Upload Hope this will Help you
public class MyWb : Activity
{
int count = 1;
IValueCallback mUploadMessage;
private static int FILECHOOSER_RESULTCODE = 1;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
};
var chrome = new FileChooserWebChromeClient ((uploadMsg, acceptType, capture) => {
mUploadMessage = uploadMsg;
var i = new Intent (Intent.ActionGetContent);
i.AddCategory (Intent.CategoryOpenable);
i.SetType ("image/*");
StartActivityForResult (Intent.CreateChooser (i, "File Chooser"), FILECHOOSER_RESULTCODE);
});
var webview = this.FindViewById<WebView> (Resource.Id.webView1);
webview.SetWebViewClient (new WebViewClient ());
webview.SetWebChromeClient (chrome);
webview.Settings.JavaScriptEnabled = true;
webview.LoadUrl ("http://www.script-tutorials.com/demos/199/index.html");
}
protected override void OnActivityResult (int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Java.Lang.Object result = intent == null || resultCode != Result.Ok
? null
: intent.Data;
mUploadMessage.OnReceiveValue (result);
mUploadMessage = null;
}
}
}
partial class FileChooserWebChromeClient : WebChromeClient
{
Action<IValueCallback, Java.Lang.String, Java.Lang.String> callback;
public FileChooserWebChromeClient (Action<IValueCallback, Java.Lang.String, Java.Lang.String> callback)
{
this.callback = callback;
}
//For Android 4.1
[Java.Interop.Export]
public void openFileChooser (IValueCallback uploadMsg, Java.Lang.String acceptType, Java.Lang.String capture)
{
callback (uploadMsg, acceptType, capture);
}
}

Unable to display and detect bluetooth devices

using Android.App;
using Android.Widget;
using Android.OS;
using Android.Bluetooth;
using Android.Content;
using Android.Views;
namespace BluetoothBLE
{
[Activity(Label = "BluetoothBLE", MainLauncher = true)]
public class MainActivity : Activity
{
//Local Bluetooth adapter
private BluetoothAdapter bluetoothAdapter = null;
//Return Intent extra
public const string Extra_Device_Address = "device_address";
//Members Field
private static ArrayAdapter<string> Device;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
//Get our UI controls from the loaded layout
TextView Text = FindViewById<TextView>(Resource.Id.BluetoothDevices);
TextView Device_Name = FindViewById<TextView>(Resource.Id.DeviceName);
Button Scan = FindViewById<Button>(Resource.Id.Scan);
TextView NewDevice = FindViewById<TextView>(Resource.Id.NewDevice);
ListView List = FindViewById<ListView>(Resource.Id.ListBluetooth);
//Initialize array adapters
Device = new ArrayAdapter<string>(this, Resource.Layout.Main);
//Testing Faced
Scan.Click += (sender, e) =>
{
if (bluetoothAdapter.IsEnabled == true)
{
Toast.MakeText(Application, "Start scanning", ToastLength.Short).Show();
DoDiscovery();
(sender as View).Visibility = ViewStates.Visible;
if(Device.Count != 0)
{
NewDevice.Text = Device.GetItem(0).ToString();
}
else
{
NewDevice.Text = "Device is not recorded";
}
Toast.MakeText(Application, "Device found", ToastLength.Short).Show();
}
else
{
Toast.MakeText(Application, "Please enable bluetooth", ToastLength.Short).Show();
}
};
//Find and set up the ListView for newly discovered devices.
List.Adapter = Device;
List.ItemClick += DeviceListClick;
//Register for broadcasts when a device is discovered
Receiver receiver = new Receiver();
var filter = new IntentFilter(BluetoothDevice.ActionFound);
RegisterReceiver(receiver, filter);
//Register for broadcasts when discovery is finished
filter = new IntentFilter(BluetoothAdapter.ActionDiscoveryFinished);
RegisterReceiver(receiver, filter);
bluetoothAdapter = BluetoothAdapter.DefaultAdapter; //Get Loacl Bluetooth Adapter
if (bluetoothAdapter == null)
{
Toast.MakeText(this, "Bluetooth is not Supported.", ToastLength.Long).Show();
Finish();
return;
}
//Display the Name of local bluetooth
Device_Name.Text = bluetoothAdapter.Name;
}
protected override void OnResume()
{
base.OnResume();
//RegisterReceiver(bluetooth, new IntentFilter(""));
}
protected override void OnPause()
{
base.OnPause();
//UnregisterReceiver(bluetooth);
}
void DeviceListClick (object sender, AdapterView.ItemClickEventArgs args)
{
// Cancel discovery because it's costly and we're about to connect
bluetoothAdapter.CancelDiscovery();
//Get the device MAC address, which is the last 17 chars in the View
var info = (args.View as TextView).Text.ToString();
var address = info.Substring(info.Length - 17);
//Create the result Intent and include the MAC address
Intent intent = new Intent();
intent.PutExtra(Extra_Device_Address, address);
//Set result and finish this Activity
SetResult(Result.Ok, intent);
Finish();
}
private void DoDiscovery()
{
//Indicate scanning in the title
SetProgressBarIndeterminateVisibility(true);
SetTitle(Resource.String.scanning);
//Turn on sub-title for new devices
FindViewById<View>(Resource.Id.NewDevice).Visibility = ViewStates.Visible;
//If we're already discovering, stop it
if (bluetoothAdapter.IsDiscovering)
{
bluetoothAdapter.CancelDiscovery();
}
//Request disccover from BluetoothAdapter
bluetoothAdapter.StartDiscovery();
}
public class Receiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
string action = intent.Action;
// When discovery finds a device
if (action == BluetoothDevice.ActionFound)
{
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
// If it's already paired, skip it, because it's been listed already
if(device.BondState != Bond.Bonded)
{
Device.Add(device.Name + "\n" + device.Address);
}
// When discovery is finished, change the Activity title
else if(Device.Count == 0)
{
Device.Add("Empty");
}
}
}
}
}
}
Testing my code, I found out that my application is unable to detect Bluetooth devices and also it is unable to display them. I went online to find examples to follow and did not find anything wrong. Any helps would be greatly appreciated. Apologies if I made small mistakes, this is my first time trying to create android application using Xamarin.

Xamarin Android Background Service crashes when the Application get closed

Hello, I want to build an app, in which you can start a service, which runs intependenly and creates a notification, and this service should constantly proof, if the DateTime.Now.Date is bigger than a spezific Date.
When I execute the code below, the notification gets displayed, but when I am closing the app, a few secondes later I get two times an information that the app crashed and I dont know why.
I cant even debug the code because this anly happens when the application is closed....
I hope you can help me thanks!
Here is my code:
namespace App
{
[Activity(Label = "App", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate {
button.Text = string.Format("{0} clicks!", count++);
StartService(new Intent(this, typeof(backgroudservice)));
};
}
}
public class backgroudservice : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
newnotification("Title", "Text: ", 0);
new Task(() => {
DoWork();
Thread.Sleep(1000);
}).Start();
return StartCommandResult.Sticky;
}
public void DoWork()
{
if (DateTime.Now.Date > Convert.ToDateTime("2016-03-29").Date)
{
cancelnotification(0);
StopSelf();
}
}
public override void OnDestroy()
{
base.OnDestroy();
cancelnotification(0);
}
private void newnotification(string titel, string text, int id)
{
Notification.Builder builder = new Notification.Builder(this)
.SetContentTitle(titel)
.SetContentText(text)
.SetSmallIcon(Resource.Drawable.droidlogo_small)
.SetAutoCancel(false)
.SetVisibility(NotificationVisibility.Public)
.SetContentIntent(PendingIntent.GetActivity(this, 0, new Intent(this, typeof(MainActivity)), PendingIntentFlags.OneShot));
// Build the notification:
Notification notification = builder.Build();
notification.Flags = NotificationFlags.NoClear;
//notification.ContentIntent = new Intent(this,typeof(login));
// Get the notification manager:
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
notificationManager.Notify(id, notification);
}
private void cancelnotification(int id)
{
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
notificationManager.Cancel(id);
}
}
}
I solved it, I forgot the [Service] above my class, now it works!
[Service]
public class backgroudservice : Service
{
...
}
You might try moving the call to cancelnotification in your service's OnDestroy to before the call to the base method, i.e.:
public override void OnDestroy()
{
cancelnotification(0);
base.OnDestroy();
}

Categories