I'd like to create notificiation in my app, which is going to be showed in 10 seconds. It works well, when application is running, but when I close the application, notification is not showed. Here is my code:
My notification service:
[Service]
class NotifyEvent : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
PendingIntent pIntent = PendingIntent.GetActivity(this, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(this);
builder.SetContentTitle(Resources.GetString(Resource.String.NotifikaceNadpis));
builder.SetContentText(Resources.GetString(Resource.String.NotifikaceText));
builder.SetSmallIcon(Resource.Drawable.Icon);
builder.SetPriority(1);
builder.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate);
builder.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis());
Notification notifikace = builder.Build();
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
const int notificationId = 0;
notificationManager.Notify(notificationId, notifikace);
}
}
Class, which starts notification:
public class Notificator
{
public void ShowNotification(Context context)
{
Intent intent = new Intent(context, typeof(NotifyEvent));
var pendingServiceIntent = PendingIntent.GetService(context, 0, intent, PendingIntentFlags.UpdateCurrent);
AlarmManager alarm = (AlarmManager)context.GetSystemService(Context.AlarmService);
alarm.Set(AlarmType.ElapsedRealtimeWakeup, SystemClock.ElapsedRealtime() + 10000, pendingServiceIntent);
}
}
Method in activity:
Notificator not = new Notificator();
not.ShowNotification(this);
My Activity:
[Activity(Label = "Nastavení")]
public class SettingsActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.Settings);
Button vynulovatButton = FindViewById<Button>(Resource.Id.buttonRestartDne);
vynulovatButton.Click += VynulovatDen;
}
...
protected void VynulovatDen(object sender, EventArgs e)
{
Notificator not = new Notificator();
not.ShowNotification(this);
}
}
Thanks for every help.
you can try this.
protected override void OnDestroy()
{
Notificator not = new Notificator();
not.ShowNotification(this);
base.OnDestroy();
}
You should keep your service alive when you destroy your application.
add return StartCommandResult.Sticky; in the OnStartCommand method.
start the service OnTaskRemoved function.
Create your service with the Service interface, the IntentService is for Time-consuming operation.
class NotifyEvent : Service
{
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
new Task(() => {
PendingIntent pIntent = PendingIntent.GetActivity(this, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(this);
builder.SetContentTitle("hello");
builder.SetContentText("hello");
builder.SetSmallIcon(Resource.Drawable.Icon);
builder.SetPriority(1);
builder.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate);
builder.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis());
Notification notifikace = builder.Build();
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
const int notificationId = 0;
notificationManager.Notify(notificationId, notifikace);
}).Start();
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartService = new Intent(ApplicationContext, typeof(NotifyEvent));
restartService.SetPackage(PackageName);
var pendingServiceIntent = PendingIntent.GetService(ApplicationContext, 0, restartService, PendingIntentFlags.UpdateCurrent);
AlarmManager alarm = (AlarmManager)ApplicationContext.GetSystemService(Context.AlarmService);
alarm.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 1000, pendingServiceIntent);
System.Console.WriteLine("service OnTaskRemoved");
base.OnTaskRemoved(rootIntent);
}
}
Related
I write simple audio player for Android. I wrote basic functional and now I want create notification for control audio player. I use Android classes Service and Notification and RemoteViews for big and small notifications. This work good, but, when I use fasted flipping for audio tracks my notification don't update text with name audio tracks. I create simple video for demonstration problem:
you-tube.com
I use C# and Xamarin Forms, but it doesn't matter, since the code is very similar to Java. My service:
[Service]
public class PlayerService : Service, IPlayerService
{
private const string ACTION_SHOW_PLAYER = "music.player.ACTION.SHOW_PLAYER";
private const string ACTION_PLAY_PAUSE = "music.player.ACTION.PLAY_PAUSE";
private const string ACTION_PLAY_PREW = "music.player.ACTION.PLAY_PREW";
private const string ACTION_PLAY_NEXT = "music.player.ACTION.PLAY_NEXT";
private const string ACTION_STOP_SERVICE = "music.player.ACTION.STOP_SERVICE";
private const string ANDROID_CHANNEL_ID = "com.companyname.homeremotecontroller";
private const int NOTIFICATION_ID = 46;
private RemoteViews _remoteViewsBig = null;
private RemoteViews _remoteViewsSmall = null;
private NotificationManager _notificationManager = null;
private NotificationCompat.Builder _builder = null;
private static IPlayerService _instance = null;
private static bool _isActiveBackgroundService = false;
private static bool _isBeenStartBackgroundService= false;
public IPlayerService GetInstance()
{
return _instance;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
_instance = this;
MusicPlaylistModel.GetInstance().ChangeSound += () =>
{
UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
};
MusicPlaylistModel.GetInstance().MusicPlayer.TimeSound += (start, end) =>
{
// TODO: empty
};
MusicPlaylistModel.GetInstance().MusicPlayer.TickCurrentSound += (curr) =>
{
UpdateProgressSound(curr);
};
MusicPlaylistModel.GetInstance().PlayingStatus += (status) =>
{
UpdatePlayPauseStatus(status);
};
_notificationManager = ((NotificationManager)GetSystemService(NotificationService));
_notificationManager.CreateNotificationChannel(new NotificationChannel(ANDROID_CHANNEL_ID, "Audio player", NotificationImportance.High));
Intent notificationIntent = new Intent(MainActivity.Activity, typeof(MainActivity));
_remoteViewsSmall = GetSmallContentView();
_remoteViewsBig = GetBigContentView();
CreateBuilderNotification();
}
public override void OnDestroy()
{
base.OnDestroy();
}
public void CreateBuilderNotification()
{
_builder = new NotificationCompat.Builder(MainActivity.Activity, ANDROID_CHANNEL_ID)
.SetCategory(Notification.ExtraMediaSession)
.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall)
.SetSmallIcon(Resource.Mipmap.icon)
.SetOngoing(true)
.SetSilent(true)
.SetPriority((int)NotificationPriority.Max)
.SetVisibility(NotificationCompat.VisibilityPublic)
.SetAutoCancel(true)
.SetStyle(new NotificationCompat.DecoratedCustomViewStyle());
}
public Notification RecreateNotification()
{
return _builder.Build();
}
private RemoteViews GetBigContentView()
{
RemoteViews mContentViewBig = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_big);
Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
closeIntent.SetAction(ACTION_STOP_SERVICE);
PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
nextIntent.SetAction(ACTION_PLAY_NEXT);
PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
prewIntent.SetAction(ACTION_PLAY_PREW);
PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusicBig, pPlayPauseIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseService, pcloseIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.playNextBig, pNextIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.playPrevBig, pPrewIntent);
return mContentViewBig;
}
private RemoteViews GetSmallContentView()
{
RemoteViews mContentViewSmall = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_small);
Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
closeIntent.SetAction(ACTION_STOP_SERVICE);
PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
nextIntent.SetAction(ACTION_PLAY_NEXT);
PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
prewIntent.SetAction(ACTION_PLAY_PREW);
PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseServiceSmall, pcloseIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusic, pPlayPauseIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayNext, pNextIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPrevious, pPrewIntent);
return mContentViewSmall;
}
public void RedrawNotification()
{
if(!_isActiveBackgroundService)
{
return;
}
var notification = RecreateNotification();
notification.Flags = NotificationFlags.NoClear | NotificationFlags.OngoingEvent;
_notificationManager.Notify(NOTIFICATION_ID, notification);
StartForeground(NOTIFICATION_ID, notification);
}
public void Start()
{
MainActivity.Activity.StartForegroundService(new Intent(MainActivity.Activity, typeof(PlayerService)));
_isActiveBackgroundService = true;
_isBeenStartBackgroundService = true;
}
public void Stop()
{
MainActivity.Activity.StopService(new Intent(MainActivity.Activity, typeof(PlayerService)));
_isActiveBackgroundService = false;
}
public void UpdateSound(string snd_name)
{
_remoteViewsSmall.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
_remoteViewsBig.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
_builder.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall);
RedrawNotification();
}
public void UpdateProgressSound(int currProgress)
{
// TODO: empty
}
public void UpdatePlayPauseStatus(bool status)
{
if(status)
{
_remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.play_off);
_remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.play_off);
}
else
{
_remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.pause_off);
_remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.pause_off);
}
_builder.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall);
RedrawNotification();
}
public override StartCommandResult OnStartCommand(Intent? intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (_isBeenStartBackgroundService)
{
UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
UpdatePlayPauseStatus(false);
RedrawNotification();
_isBeenStartBackgroundService = false;
}
string action = intent.Action;
if (action != null)
{
if (action == ACTION_STOP_SERVICE)
{
Stop();
}
else
{
new Task(async () =>
{
if (action == ACTION_PLAY_PAUSE)
{
await MusicPlaylistModel.GetInstance().Play();
return;
}
else if (action == ACTION_PLAY_PREW)
{
await MusicPlaylistModel.GetInstance().Prew();
return;
}
else if (action == ACTION_PLAY_NEXT)
{
await MusicPlaylistModel.GetInstance().Next();
return;
}
}).Start();
}
}
return StartCommandResult.Sticky;
}
}
To show the notification, the external code calls the Start method, and to hide the notification, the external code calls the Stop method.
I check for debugging. Application successful will hit in method UpdateSound, where snd_name - is correct. But text in Notification is not correct.
How fix it?
In UpdateSound() you call RedrawNotification()
In RedrawNotification() you call RecreateNotification()
In RecreateNotification() you call _builder.Build()
But you never made any changes in _builder.
The solution: use Notification.MediaStyle class.
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.
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
I'm developing an Android application with Xamarin Forms that is composed of an interface and also a background service.
I need that the service works also when the interface application is closed.
If I add "IsolatedProcess = true" into the service the graphical interface still works but the service crashes.
I read a lot of posts with possible solutions but they don't work. (I tried to compile in release mode and also to remove "Use Shared Runtime" flag).
I'm compiling with Android 8.1 (Oreo) as Target Framework.
The target environment is Android 4.2.
I start the service into OnCreate method of the MainActivity class:
Intent testIntent = new Intent(this.BaseContext, typeof(TestService));
StartService(testIntent);
The service class:
[Service(IsolatedProcess = true, Exported = true, Label = "TestService")]
public class TestService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Device.StartTimer(new TimeSpan(0, 0, 40), () =>
{
//Code executed every 40 seconds
});
base.OnStartCommand(intent, flags, startId);
return StartCommandResult.Sticky;
}
public override bool StopService(Intent name)
{
return base.StopService(name);
}
}
If I remove "IsolatedProcess = true" the service works but it will be stopped when I will close the application interface process.
I solved the issue by changing the value of the attribute IsolatedProcess to true, removing the Device.StartTimer instruction and by introducing a BroadcastReceiver.
MainActivity class:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Intent testServiceIntent;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
testServiceIntent = new Intent(this.BaseContext, typeof(TestService));
LoadApplication(new App());
}
}
The service class:
[Service(IsolatedProcess = false, Exported = true, Label = "TestService")]
public class TestService : Service
{
System.Threading.Timer _timer;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
businessLogicMethod();
base.OnStartCommand(intent, flags, startId);
return StartCommandResult.Sticky;
}
public void businessLogicMethod()
{
//My business logic in a System.Threading.Timer
}
}
The Broadcast Receiver class:
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class TestApplicationBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Log.Info("TestApp", "******* Loading Application *******");
try
{
if (intent.Action.Equals(Intent.ActionBootCompleted))
{
Intent service = new Intent(context, typeof(TestService));
service.AddFlags(ActivityFlags.NewTask);
context.StartService(service);
}
}
catch (Exception ex)
{
Log.Error("TestApp", "******* Error message *******: " + ex.Message);
}
}
}
I hope that can be useful for someone.
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();
}