Xamarin.Android : start an activity and wait for the result - c#

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();
}
}

Related

Android Services - Unable to stop after application close

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.

Creating a notification at a specific time

I have a calendar app that I'm working on, and I need to alert the user to an Upcoming event.
so I want to know what's the best practice to do this, and how should I do it, since the current solution I have isn't even working very well.
the way I do it now is by using an AlarmManager to trigger the a BroadcasReciever that creates and shows the notification.
do I need to use a service? and if so, how should I do it?
CODE:
private void StartDBUpdateAlarm() // Start the alarm manager for event reminder
{
// Get the eventos orded by date (Later than DateTime.Now),
// so that we can get the earliest events
var eventos = eventDao.Select(FromEventos.OrderByDate, DateTime.Now);
if ((eventos.Count > 0)) // Only create notification if there is an event
{
// Create the Intent to pass some info through it to the notification
Intent alarmIntent = new Intent(this, typeof(ReminderReciever));
// Putting the information that will be passed with constant Id's
alarmIntent.PutExtra(GerirUtils.INTENT_TITLE, GetString(Resource.String.main_notification_title));
alarmIntent.PutExtra(GerirUtils.INTENT_INFO, "Info");
alarmIntent.PutExtra(GerirUtils.INTENT_CONTENT, eventos[0].Descricao);
// Creating/Recreating the Pending Intent that will pass the actual information to the notification
PendingIntent pendingIntent = PendingIntent.GetBroadcast(this, GerirUtils.NOTIFICATION_REMINDER_ID, alarmIntent, 0);
// Getting the date and time of the next event, then calculate the diference between DateTime.Now,
// to know how much time it'll be until next event, so that we know when to trigger the notification
DateTime eventDateTime = CalendarHelper.EventDateTime(eventos[0]);
TimeSpan eventMillis = (eventDateTime - GerirUtils.BaseUTCDate); // BaseDate is just the Utc date = 1970/01/01, because the alarm counts millis since then
// Creating and setting the alarm manager
alarm = (AlarmManager)GetSystemService(Context.AlarmService);
alarm.SetExact(AlarmType.RtcWakeup, (long)eventMillis.TotalMilliseconds, pendingIntent);
}
}
BROADCAST RECIEVER CLASS
[BroadcastReceiver(Enabled = true)]
public class ReminderReciever : BroadcastReceiver
{
private string type, title, info, content;
private IList<string> events;
private int notificationId;
private Context context;
public override void OnReceive(Context context, Intent intent)
{
this.context = context;
notificationId = intent.GetIntExtra(GerirUtils.INTENT_ID, -1);
type = intent.GetStringExtra(GerirUtils.INTENT_TYPE);
events = intent.GetStringArrayListExtra(GerirUtils.INTENT_EVENTS);
title = intent.GetStringExtra(GerirUtils.INTENT_TITLE);
info = intent.GetStringExtra(GerirUtils.INTENT_INFO);
content = intent.GetStringExtra(GerirUtils.INTENT_CONTENT);
if(notificationId > -1)
if(type == GerirUtils.NOTIFICATION_TYPE_EVENT_REMINDER)
ShowNotification();
else if(type == GerirUtils.NOTIFICATION_TYPE_ADDED_EVENTS)
ShowNotificationList();
}
private void ShowNotification()
{
Android.Net.Uri sound = RingtoneManager.GetDefaultUri(RingtoneType.Alarm);
NotificationManager mNotificationManager = (NotificationManager)context.GetSystemService(Context.NotificationService);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
NotificationChannel channel = new NotificationChannel("default", title, NotificationImportance.Default) { Description = content };
mNotificationManager.CreateNotificationChannel(channel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, "default")
.SetSmallIcon(Resource.Mipmap.ic_calendario_gerir) // notification icon
.SetContentTitle(title) // title for notification
.SetContentText(content) // message for notification
.SetContentInfo(info) // Info message next to content
.SetSound(sound) // set alarm sound for notification
.SetAutoCancel(true); // clear notification after click
Intent intent = new Intent(context, typeof(MainActivity));
PendingIntent pi = PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.UpdateCurrent);
mBuilder.SetContentIntent(pi);
mNotificationManager.Notify(0, mBuilder.Build());
}
}
Well,I managed to find the problem after carefully inspecting the code, it looked like I wasn't sending an notificationId, through the intent. the the intent on the BroadcastReviever was always the default -1, so the if statement was always false and, dumb ol' me, didn't put a message warning when the Id was -1, so I had a harder time to find the error then what I would've if I had the message.
and so, the solution was adding this line of code to the program:
private void StartDBUpdateAlarm() // Start the alarm manager for event reminder
{
...
...
if ((eventos.Count > 0)) // Only create notification if there is an event
{
// Create the Intent to pass some info through it to the notification
Intent alarmIntent = new Intent(this, typeof(ReminderReciever));
// MISSING LINE OF CODE
alarmIntent.PutExtra(GerirUtils.INTENT_ID, GerirUtils.NOTIFICATION_REMINDER_ID);
...
...
...
}
}

Start Sound on BroadCast Recieving in Xamarin Android

MainActivity.cs
private void StartAlarm()
{
Intent myIntent;
PendingIntent pendingIntent; myIntent = new Intent(this, typeof(AlarmToastReceiver));
pendingIntent = PendingIntent.GetBroadcast(this, 0, myIntent, 0);
alarm_manager.Set(AlarmType.RtcWakeup, calndr.TimeInMillis, pendingIntent);
}
AlarmToastReceiver.cs
[BroadcastReceiver(Enabled =true)]
public class AlarmToastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "THIS IS MY ALARM", ToastLength.Long).Show();
}
}
I am going to create an Alaram App in Xamarin. So here i pick the Time from TimePicker, and then and set the Alaram Manager Instance. When the Pending Intent completed , then BroadCastReceiver is active, and a Message is Shown to me,i.e "THIS IS MY ALARM", but here i want to Start Some Kind of Sound, i.e Animal Sound,Bird Sound etc.So how i can do it,can any one help me?thanks in advance.
You can use MediaPlugin documented here.
Here is nice blog post, which I reccomend to read.

Chang Phone to Silent Mode on BroadCast Recieving in Xamarin Android

MainActivity.cs
private void StartAlarm()
{
Intent myIntent;
PendingIntent pendingIntent; myIntent = new Intent(this, typeof(AlarmToastReceiver));
pendingIntent = PendingIntent.GetBroadcast(this, 0, myIntent, 0);
alarm_manager.Set(AlarmType.RtcWakeup, calndr.TimeInMillis, pendingIntent);
}
AlarmToastReceiver.cs
[BroadcastReceiver(Enabled =true)]
public class AlarmToastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "BroadCast Received.", ToastLength.Long).Show();
}
}
I want to Change my Phone mode to Silent at Specific Time automatically on Broadcast receiving, So here i pick the Time from Time-picker, and then set the Ala ram Manager Instance. When the Pending Intent completed , then Broadcast Receiver is active, and a Message is Shown to me,i.e "Broadcast Received.", but here i want to change my Mobile Mode to silent Mode,So how can i do it,can any one help me ? thanks in advance.
You can use the AudioManager to set Silent/Vibrate/Normal.
var audioMgr = (AudioManager)GetSystemService(AudioService);
audioMgr.RingerMode = RingerMode.Silent; // In Oreo(+) this will enable DnD mode
From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed unless the app has been granted Do Not Disturb Access.
Re: AudioManager

Pending Intent Extras are not set for Launcher Application

I have created a Launcher application named HomeActivity which has a LaunchMode = SingleTask and CATEGORY_HOME and CATEGORY_LAUNCHER. This activity in turn starts new activities and some other applications as well.
The activity is connected with a Firebase Messaging service to get Push notifications. The Firebase service adds some extras (gathered from the push notification and sends them in Extras in HomeActivity
public override void OnMessageReceived(RemoteMessage message)
{
Notification.Builder builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.Icon);
Intent intent = new Intent(this, typeof(HomeActivity));
intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
intent.PutExtra(General.UPDATE_NOTIFIED, true);
if(message.Data != null)
{
if (message.Data.Any())
{
foreach(KeyValuePair<string,string> kv in message.Data)
{
intent.PutExtra(kv.Key, kv.Value);
}
}
}
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
builder.SetContentIntent(pendingIntent);
builder.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.app_logo));
builder.SetContentTitle(message.GetNotification().Title);
builder.SetContentText(message.GetNotification().Body);
builder.SetDefaults(NotificationDefaults.Sound);
builder.SetAutoCancel(true);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(1, builder.Build());
}
Whenever the HomeActivity is on top and a push notification is received and clicked, I can access the Extras in HomeActivity's OnResume function (I have overriden OnNewIntent(Intent intent)).
protected override void OnNewIntent(Intent intent)
{
bool updateNotification = intent.GetBooleanExtra(General.UPDATE_NOTIFIED, false); //Check if Extra is set
this.Intent = intent;
base.OnNewIntent(intent);
}
But when I am in another activity which is launched by the HomeActivity and a Push notification is clicked; the app does return to the HomeActivity but there are no Extras in the Intent.
I have tried all sorts of Flags, including but not limited to NewTask, ClearTask as well.
I am still unable to get why the Extras aren't being set when the notification is clicked at the time another activity is in place. What am I missing here.
Ok so I figured out what the issue is.
The Firebase system WILL NOT fire the OnMessageReceived method if you post a Notification with data (That is a display message).
What is needed to be done is that only data payload in the push notification will fire the OnMessageRecieved function which is responsible for setting the data.
So the notification should be like
{
to: "topic | Registered device",
data:{
data1: "data_value1",
data2: "data_value2",
data3: "data_value3",
}
}

Categories