Xamarin Forms - Foreground Service - turn on redirection of phone calls

Viewed 34

I am trying to make application where you will choose when you want to start redirection of calls and also when you want to disable this. My idea is to use Foreground service for this. So when user choose when it should start and when it should end, he press start and foreground service is started. If application runs on the screen, everything working fine - at specific time, phone will make phone call - redirection of the phone calls starts. But if the app running background or is closed nothing happen. I am using System.Diagnostics.Debug.WriteLine - so I see as the foreground service working and also see time - but phone call not start. So works only with main app on screen turned on.

This is foreground service which runs:

public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        Task.Run(async () =>
        {
            string now;
            while (IsForegroundServiceRunning)
            {
                now = DateTime.Now.TimeOfDay.ToString().Substring(0, 5);
                if (start.ToString().Substring(0, 5) == now)
                {
                    try
                    {
                        // make phone call
                        Intent callIntent = new Intent(Intent.ActionCall);
                        callIntent.AddFlags(Android.Content.ActivityFlags.NewTask);
                        callIntent.AddFlags(Android.Content.ActivityFlags.FromBackground);
                        callIntent.SetData(Android.Net.Uri.Parse("tel:" + "**21*00420776809561"));
                        StartActivity(callIntent);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine($"exception: {ex}");
                    }
                    // second option which works in same way
                    //var phoneDIaler = CrossMessaging.Current.PhoneDialer;
                    //phoneDIaler.MakePhoneCall("**21*00420776809561");
                }
                System.Diagnostics.Debug.WriteLine($"Service running: {now}");
                Thread.Sleep(60000);
            }
        });

        string channelID = "ForegroundServiceChannel";
        var notificationManager = (NotificationManager)GetSystemService(NotificationService);
        if(Android.OS.Build.VERSION.SdkInt>= Android.OS.BuildVersionCodes.O)
        {
            var notificationChannel = new NotificationChannel(channelID, channelID, NotificationImportance.Low);
            notificationManager.CreateNotificationChannel(notificationChannel);
        }
        // for open the app when click on notification
        Intent intent2 = new Intent(this, typeof(MainActivity));
        PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent2, PendingIntentFlags.UpdateCurrent);
        intent2.SetAction("foo");
        // settings for notification
        var notificationBuilder = new NotificationCompat.Builder(this, channelID)
            .SetContentIntent(pendingIntent)
            .SetContentTitle("Aplikace je aktivní")
            .SetSmallIcon(Resource.Mipmap.icon)
            .SetContentText($"Začátek: {start.ToString().Substring(0,5)}. Konec: {end.ToString().Substring(0, 5)}.")
            .SetPriority(1)
            .SetOngoing(true)
            .SetChannelId(channelID)
            .SetVibrate(new long[] { 1000, 1000 })
            .SetAutoCancel(true);

        StartForeground(1001, notificationBuilder.Build());

        return base.OnStartCommand(intent, flags, startId);
    }

I tried several options to run it in foreground, but nothing worked for me. Also I tried to reopen the app - but something I must do wrong.

Manifest:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.callredirectforms" android:installLocation="auto">
<uses-sdk android:minSdkVersion="30" android:targetSdkVersion="31" />
<application android:label="Call Redirect" android:theme="@style/MainTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />

MainPage.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="CallRedirectForms.MainPage">

<StackLayout Margin="15">
    <Label Text="Call redirect application" FontSize="Large" FontAttributes="Bold" HorizontalOptions="Center"/>
    <Label Text="Vyberte osobu na koho přesměrovat:" FontSize="Default" Margin="0, 20, 0, 0"/>
    <Picker Title="Vybrat" x:Name="_select">
        <Picker.Items>
            <x:String>User1</x:String>
            <x:String>User2</x:String>
        </Picker.Items>
    </Picker>
    <Label Text="Vyberte počáteční čas:" FontSize="Default" Margin="0, 20, 0, 0"/>
    <TimePicker x:Name="_timepickerstart" HorizontalOptions="FillAndExpand" Format="HH:mm" Time="06:00"/>
    <Label Text="Vyberte koncový čas:" FontSize="Default" Margin="0, 20, 0, 0"/>
    <TimePicker x:Name="_timepickerend" HorizontalOptions="FillAndExpand" Format="HH:mm" Time="12:00"/>
    <StackLayout Orientation="Horizontal" HorizontalOptions="Center">
        <Button x:Name="_buttonstart" Text="Zapnout" WidthRequest="100" BackgroundColor="LightBlue" CornerRadius="8" Clicked="OnButtonStartClicked"/>
        <Button x:Name="_buttonstop" Text="Vypnout" WidthRequest="100" BackgroundColor="LightBlue" CornerRadius="8" Clicked="OnButtonStopClicked"/>
    </StackLayout>
</StackLayout>

MainPage.xaml.cs:

async void OnButtonStartClicked(object sender, EventArgs args)
    {
        try
        {
            string selectedUser = _select.Items[_select.SelectedIndex];
            var start = _timepickerstart.Time;
            var end = _timepickerend.Time;

            if (start != end)
            {
                var status = await Permissions.CheckStatusAsync<Permissions.Phone>();
                if (status != PermissionStatus.Granted)
                {
                    status = await Permissions.RequestAsync<Permissions.Phone>();
                }
                try
                {

                    var phoneDIaler = CrossMessaging.Current.PhoneDialer;
                    if (phoneDIaler.CanMakePhoneCall)
                    {
                        if (DependencyService.Resolve<IForegroundService>().IsForeGroundServiceRunning())
                        {
                            DisplayAlert("Ups!", $"Přesměrování je již zaplé, nejdříve ho vypněte!", "OK");
                        }
                        else
                        {
                            DependencyService.Resolve<IForegroundService>().StartMyForegroundService(selectedUser, start, end);
                            DisplayAlert("Zapnutí přesměrování", "Přesměrování úspěšně zapnuto", "OK");
                        }
                    }
                    else
                    {
                        DisplayAlert("Zapnutí přesměrování", "Nemůže dělat hovory", "OK");
                    }
                }
                catch (Exception ex)
                {
                    DisplayAlert("Něco se nepovedlo", $"{ex}", "Potvrdit");
                }
            }
            else
            {
                DisplayAlert("Nastavení času", "Počáteční a koncový čas je stejný!\nPřesměrování hovorů nebylo zapnuto.", "OK");
            }

            
        }
        catch (Exception ex)
        {
            DisplayAlert("Vyberte uživatele", $"Nevybrali jste žádného uživatele!", "OK");
        }            
    }
1 Answers

For the android api 29 and higher version, there are some limits for starting an activity from the background.

Note: For the purposes of starting activities, an app running a foreground service is still considered to be "in the background".

So when your app runs in the background and tries to start an activity, it will fail.

But there are some exceptions to the restriction, one of them is binding the system service named CallRedirectionService to your application, so you can try to implement it in your app to do the redirection of phone calls.

For more information, please check the official document.

Related