My problem is the following . I have created android service which is winning even after the application is closed. When the application is active i can scan for the service and i can see it running and i can stop it. When i close the activity completely and start the app I again scan for active services i cannot find it in the list. So how to stop the service after the restart of the program so i can get indication if service is still running in the background. I use the bellow method for checking the service.
public void IsServiceRunning()
{
ActivityManager activityManager = (ActivityManager)GetSystemService(Context.ActivityService);
var list = activityManager.GetRunningServices(int.MaxValue);
foreach(var item in list)
{
Toast.MakeText(ApplicationContext, item.Service.ClassName + " " + item.Started + " " + item.Service.Class, ToastLength.Short).Show();
Log.Debug("++++++++++++++++++++++++++++++++++ ", item.Service.ClassName + " " + item.Started + " " + item.Service.Class);
if ( item.Service.ClassName.Contains("MyService"))
{
MessagingCenter.Send<string>("true", "CheckRunningService");
//Toast.MakeText(ApplicationContext, item.Service.ShortClassName.ToString(), ToastLength.Short).Show();
return;
}
}
MessagingCenter.Send<string>("false", "CheckRunningService");
}
Service Code
class MyService : Service
{
SmsBroadcastRceiver sms;
public override void OnCreate()
{
base.OnCreate();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
//Toast.MakeText(this, "MyService Started", ToastLength.Long).Show();
sms = new SmsBroadcastRceiver();
RegisterReceiver(sms, new IntentFilter("SMS_RECEIVED"));
return StartCommandResult.NotSticky;
}
public override void OnDestroy()
{
UnregisterReceiver(sms);
StopSelf();
//Toast.MakeText(this, "MyService Stopped", ToastLength.Long).Show();
base.OnDestroy();
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
Try to put android:stopWithTask="true" with service,which is declared in manifest file and
In the service class
Override the onTaskRemoved method and check also you can remove updates of location listener here to stop the location updates
Hope it will help for you
I think i found why once my app is stopped i cannot find it. As stated bellow from the documentation :
getRunningServices(int maxNum)
This method was deprecated in API level 26. As of Build.VERSION_CODES.O, this method is no longer available to third party applications. For backwards compatibility, it will still return the caller's own services.
Related
Im using this code to get location updates for my Xamarin Android app. I have 2 questions.
How to make this foreground service to run forever? I've tried to change return StartCommandResult.NotSticky; to return StartCommandResult.Sticky; and remove anything from OnDestroy() method, but OS seems to be unable to recreate service after it was killed or crashed. So, it runs about a half day only, even i've added app to my battery optimization exclude list. How to make it run forever?
How to properly start service from boot?
Here is what i've tried. Added following to MainActivity.cs
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
public Context Context { get; set; }
public override void OnReceive(Context context, Intent intent)
{
var stepServiceIntent = new Intent(context, typeof(LocationUpdatesService));
stepServiceIntent.PutExtra("StartedFromBoot", true);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(stepServiceIntent);
}
else
{
context.StartService(stepServiceIntent);
}
}
}
Edited LocationUpdatesService.cs
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Info(Tag, "Service started");
var startedFromNotification = intent.GetBooleanExtra(ExtraStartedFromNotification, false);
var startedFromBoot = intent.GetBooleanExtra("StartedFromBoot", false);
if (startedFromBoot)
{
//Preferences.Set("LocationUpdates", true);
StartForeground(NotificationId, StartNotification("",""));
Preferences.Set("foreground", true);
try
{
FusedLocationClient.RequestLocationUpdates(LocationRequest, LocationCallback, Looper.MyLooper());
}
catch (SecurityException unlikely)
{
Preferences.Set("LocationUpdates", false);
Log.Error(Tag, "Lost location permission. Could not request updates. " + unlikely);
}
}
if (startedFromNotification)
{
RemoveLocationUpdates();
StopSelf();
}
return StartCommandResult.Sticky;
}
I got only single location update that way right from boot. After that single update Im geting "Unknown Location" so service doesnt run continuously. So, how to properly start that service from boot to make it run continuously?
Maybe there will be a single solution for both questions, so if there is a way to start fully functional service from boot, then system could recreate it with Sticky flag and run forever.
Actually, the foreground service will keep running when the phone is on. But you can also use the PowerManager.WakeLock to make sure your app always keep alive even the device is sleep.
You can check this case:Xamarin wakelock
In addition, it seems that you want to get the user's location cyclically. So you can run a timed task in the foreground service. There are many ways to do that. Such as:
JobScheduler
AlarmManager
WorkManager
ScheduledThreadPoolExecutor
You can check this case:Xamarin Android - Periodic task execution
My problem is how to get ssid and password of my hotspot to share with receiver ?
Hello I am developing app in xamarin forms to share files with each other.
I got success to turn on and off wifi and hotspot.
But when i turn on hotspot it turn on hotspot with different ssid and password which i want to know and turn it into barcode so receiver will scan code and connect to hotspot and share files.
My code runs very well but don't know how to solve this problem.
You could custom the WifiManager.LocalOnlyHotspotCallback, and there is a onStarted method that can get WifiConfiguration. Then we can get the SSID and Password now.
For example, CustomWifiManagerCallback class as follows:
public class CustomWifiManagerCallback: WifiManager.LocalOnlyHotspotCallback
{
private const string TAG = nameof(myWifiManagerCallback);
private MainActivity mainActivity;
public CustomWifiManagerCallback(Activity _activity)
{
if (_activity.GetType() == typeof(MainActivity))
mainActivity = (MainActivity)_activity;
}
public override void OnStarted(WifiManager.LocalOnlyHotspotReservation reservation)
{
base.OnStarted(reservation);
Console.WriteLine( "Wifi Hotspot is on now");
var ssid = reservation.WifiConfiguration.Ssid;
var pwd = reservation.WifiConfiguration.PreSharedKey;
Console.WriteLine("started SSID:" + ssid + " password:"+ pwd);
mainActivity.Mreservation = reservation;
}
public override void OnFailed([GeneratedEnum] LocalOnlyHotspotCallbackErrorCode reason)
{
base.OnFailed(reason);
Console.WriteLine( "onStopped: ");
}
public override void OnStopped()
{
base.OnStopped();
Console.WriteLine( "onFailed: ");
}
}
Now we can start as follows:
WifiManager wifi = (WifiManager)Android.App.Application.Context.GetSystemService(Context.WifiService);
wifi.StartLocalOnlyHotspot(new CustomWifiManagerCallback(this), new Handler());
I need to start an activity to ask the user to grant the app battery saving exemption. The user must respond yes to be able to continue:
Here is the -buggy- code I have so far:
PowerManager pm = (PowerManager)Android.App.Application.Context.GetSystemService(Context.PowerService);
while (!pm.IsIgnoringBatteryOptimizations(this.ApplicationContext.PackageName))
{
IPopupService popupService = Locator.GetSharedInstance<IPopupService>();
await popupService.DisplayAlertAsync(
BusinessResources.GoToPowerSettingsTitle,
BusinessResources.GoToPowerSettingsMessage,
BusinessResources.Ok).ConfigureAwait(true);
using (var intent = new Intent(Android.Provider.Settings.ActionRequestIgnoreBatteryOptimizations, Android.Net.Uri.Parse("package:" + Forms.Context.PackageName)))
{
Forms.Context.StartActivity(intent);
}
}
Because StartActivity won't wait, I'll ask the user once more than needed.
Now I've searched for a possible solution and found ActivityTask and ActivityController.
I am confused right now. Which one should I use, provided that Xamarin Forms is on the way?
All right, I solved my problem by awaiting an AsyncAutoResetEvent after launching the activity:
An event is defined as well as a handshake value:
private AsyncAutoResetEvent dozeStandby;
private int dozeStandbyHandshake = nameof(dozeStandbyHandshake).GetHashCode(StringComparison.InvariantCultureIgnoreCase);
During the activity creation, I'm prompting the user to allow the settings.
Because I'm using StartActivityForResult, OnActivityResult will be called when the prompt activity finishes:
protected override async void OnCreate(Bundle bundle)
{
dozeStandby = new AsyncAutoResetEvent();
PowerManager pm = (PowerManager)Android.App.Application.Context.GetSystemService(Context.PowerService);
while (!pm.IsIgnoringBatteryOptimizations(this.ApplicationContext.PackageName))
{
IPopupService popupService = Locator.GetSharedInstance<IPopupService>();
await popupService.DisplayAlertAsync(
BusinessResources.GoToPowerSettingsTitle,
BusinessResources.GoToPowerSettingsMessage,
BusinessResources.Ok).ConfigureAwait(true);
using (var intent = new Intent(Android.Provider.Settings.ActionRequestIgnoreBatteryOptimizations, Android.Net.Uri.Parse("package:" + Android.App.Application.Context.PackageName)))
{
this.StartActivityForResult(intent, dozeStandbyHandshake);
await this.dozeStandby.WaitAsync().ConfigureAwait(true);
}
}
}
In here I check the handshake to set the event.
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == dozeStandbyHandshake)
{
this.dozeStandby.Set();
}
}
the problem I have here might have a quick workaround but I've been stuck for a while.
I am writing a service that starts a process when a user logs in and stops it when the user logs out. In my previous tries I have tried using a loop which checked the presence of explorer.exe but it didn't work very well as the infinite loop meant the service was stuck at "starting". So I have looked into the C# on session change. Here is the code I currently have, creating text files to check if it actually works.
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "Service1";
}
public void onDebug()
{
OnStart(null);
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
EventLog.WriteEntry("SimpleService.OnSessionChange", DateTime.Now.ToLongTimeString() +
" - Session change notice received: " +
changeDescription.Reason.ToString() + " Session ID: " +
changeDescription.SessionId.ToString());
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "sessionchange.txt");
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
string[] lines = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged in.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedin.txt", lines);
break;
case SessionChangeReason.SessionLogoff:
string[] lines2 = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged out.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedout.txt", lines2);
break;
}
}
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "onstart.txt");
}
I expected this to work, but apparently not. The on start text file is being created, but out of the 4 logged on/off ones that should appear (somewhere) only loggedout.txt appears in the Public user directory after a log out. No matter how many times I shut down, log in or out the other text files for login are not being created. The time in the only loggedout.txt file create also won't update.
If it helps, I'm running this in a Virtual Machine. I have also read somewhere that SessionChange might only apply to remote log ins but I'm not sure. I'm not sure how to proceed, wether to keep trying or there is another way for services to "listen" to session changes?
Thanks for taking the time to read through.
I created a xamarin form project and I integrated firebase push notification in both Android & IOS projects. Its working fine on Android but not working with iOS.
I downloaded and added GoogleService-info.plist in iOS project, Set its Build Action to BundleResource.
AppDelegates.cs
namespace PushNotification.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
RegisterForNotificationFCM();
return base.FinishedLaunching(app, options);
}
private void RegisterForNotificationFCM()
{
//Firebase Cloud Messaging Configuration
//Get permission for notification
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS 10
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
Console.WriteLine(granted);
});
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
Messaging.SharedInstance.RemoteMessageDelegate = this as IMessagingDelegate;
}
else
{
// iOS 9 <=
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Firebase.Analytics.App.Configure();
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var newToken = Firebase.InstanceID.InstanceId.SharedInstance.Token;
System.Diagnostics.Debug.WriteLine(newToken);
connectFCM();
});
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Messaging.SharedInstance.Disconnect();
}
public override void OnActivated(UIApplication uiApplication)
{
connectFCM();
base.OnActivated(uiApplication);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken(deviceToken, Firebase.InstanceID.ApnsTokenType.Prod);
}
//Fire when background received notification is clicked
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
//Messaging.SharedInstance.AppDidReceiveMessage(userInfo);
System.Diagnostics.Debug.WriteLine(userInfo);
// Generate custom event
NSString[] keys = { new NSString("Event_type") };
NSObject[] values = { new NSString("Recieve_Notification") };
var parameters = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(keys, values, keys.Length);
// Send custom event
Firebase.Analytics.Analytics.LogEvent("CustomEvent", parameters);
if (application.ApplicationState == UIApplicationState.Active)
{
System.Diagnostics.Debug.WriteLine(userInfo);
var aps_d = userInfo["aps"] as NSDictionary;
var alert_d = aps_d["alert"] as NSDictionary;
var body = alert_d["body"] as NSString;
var title = alert_d["title"] as NSString;
debugAlert(title, body);
}
}
private void connectFCM()
{
Messaging.SharedInstance.Connect((error) =>
{
if (error == null)
{
Messaging.SharedInstance.Subscribe("/topics/topicName");
}
System.Diagnostics.Debug.WriteLine(error != null ? "error occured" : "connect success");
});
}
private void debugAlert(string title, string message)
{
var alert = new UIAlertView(title ?? "Title", message ?? "Message", null, "Cancel", "OK");
alert.Show();
}
}
}
Added all required Firebase libraries in IOS project & its building fine. But notification is not receiving on IOS simulator. Tell me what I am missing.
SOLVED
I had this same problem and was dealing with it for a day or two. The problem came down to the selected provisioning profile that was being used. When I changed my app id in the apple development portal to use push notification and downloaded my provisioning profile it created a second profile with the same name downloaded. This caused an error when it tried to select the correct one. Deleted the old provisioning profile and all is well now.
You cannot test push notification in the simulator
Please take a look on the Prerequisites.
For Cloud Messaging:
- A physical iOS device
- APNs certificate with Push Notifications enabled
- In Xcode, enable Push Notifications in App > Capabilities