Search code examples
c#xamarinxamarin.formsxamarin.android

Is there a way to create a timeout to an attempt to connection with Xamarin.Forms?


I'm trying to develop a warning if I try to connect to a specific SSID and some waiting time has passed. I've tried with a Timer class but there is some issues with Task and Threads I can't resolve.

This is my Wifi class in Xamarin.Droid

public class Wifi : Iwifi
{
    private Context context;
    private static WifiManager _manager;
    private MyReceiver _receiver;

    public void Initialize()
    {
        context = Android.App.Application.Context;
        _manager = (WifiManager)context.GetSystemService(Context.WifiService);
        _receiver = new MyReceiver();
    }

    public void Register()
    {
        IntentFilter intents = new IntentFilter();
        intents.AddAction(WifiManager.ScanResultAction);
        intents.AddAction(WifiManager.NetworkStateChangedAction);
        context.RegisterReceiver(_receiver, intents);
    }

    public void Unregister()
    {
        context.UnregisterReceiver(_receiver);
    }

    public void ScanWirelessDevices()
    {
        _manager.StartScan();
    }

    public string GetConnectionSSID()
    {
        return _manager.ConnectionInfo.SSID;
    }

    public void ConnectToSSID(string SSID, string pwd)
    {
        if (!_manager.IsWifiEnabled)
        {
            _manager.SetWifiEnabled(true);
        }
        WifiConfiguration wifiConfiguration = new WifiConfiguration();
        wifiConfiguration.Ssid = '"' + SSID + '"';
        if (pwd.Empty)
        {
            wifiConfiguration.AllowedKeyManagement.Set((int)KeyManagementType.None);
        }
        else
        {
            //Configuration for protected Network
        }
        var addNet = _manager.AddNetwork(wifiConfiguration);
        if (addNet == -1)
        {
            _manager.Disconnect();
            _manager.EnableNetwork(addNet, true);
            _manager.Reconnect();
            return;
        }
        var list = _manager.ConfiguredNetworks;
        foreach (WifiConfiguration conf in list)
        {
            if (conf.Ssid.Equals('"' + SSID + '"'))
            {
                _manager.Disconnect();
                _manager.EnableNetwork(conf.NetworkId, true);
                _manager.Reconnect();
                return;
            }
        }
    }

    public class MyReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            if (intent.Action.Equals(WifiManager.ScanResultAvailableAction))
            {
                IList<ScanResult> scanResult = _manager.ScanResult;
                App.Networks.NetworksList.Clear();
                foreach (ScanResult result in scanResult)
                {
                    App.Networks.NetworksList.Add(result.Ssid);
                }
            }
        }
    }
}

Then this is a part of App class in Xamarin.Forms

public partial class App: Application
{
    private static ...
    .
    .
    .
    private static string _selectedSSID;
    private static MainDetail _pageDetail;

    public static IWifi WifiManager { get; } = DependencyService.Get<Iwifi>();
    public static string SelectedSSID { get { return _selectedSSID; } set { _selectedSSID = value; } }
    public static MainDetail PageDetail { get { return _pageDetail; } }

    public App()
    {
        InitializeComponent();
        WifiManager.Initialize();
        WifiManager.Register();
        InitViews();
        MainPage = _mainPage;
        Connectivity.ConnectivityChanged += NetworkEvents;
        NetSearch();
    }

    .
    .
    .
   
    public void NetSearch()
    {
        Task.Run(async () =>
        {
            while (true)
            {                 
                WifiManager.ScanWirelessDevices();
                await Task.Delay(Utility.SCAN_WIFI_TIMER); //waiting 31000 milliseconds because of Scanning throttling
            }
        });
    }

    public void NetworkEvents(object sender, ConnectivityChangedEventArgs e)
    {
        MainMaster master = (MainMaster)_mainPage.Master;

        if (e.NetworkAccess == NetworkAccess.Unknown)
        {
            Debug.WriteLine("Network Access Unknown " + e.ToString());
        }
        if (e.NetworkAccess == NetworkAccess.None)
        {
            Debug.WriteLine("Network Access None " + e.ToString());
        }
        if (e.NetworkAccess == NetworkAccess.Local)
        {
            Debug.WriteLine("Network Access Local " + e.ToString());
        }
        if (e.NetworkAccess == NetworkAccess.Internet)
        {
            if(selectedSSID == Wifimanager.GetConnectionInfo())
            {
                //WE CONNECTED!!
                //Now I want to stop the Timeout Timer to attempt
            }
        }
        if (e.NetworkAccess == NetworkAccess.ConstrainedInternet)
        {
            Debug.WriteLine("Network Access Constrainde Internet " + e.ToString());
        }
    }
}

And part of Detail page class in which I start the event of connection and where I want to start also the timeout timer

public partial class MainDetail : ContentPage
{
    .
    .
    .

    public void OnItemListClicked(object sender, SelectedItemChangedEventArgs e)
    {
        if (e.SelectedItem == null)
        {
            return;
        }
        ImageCell item = (ImageCell)e.SelectedItem;
        App.SelectedSSID = item.Text;
        App.WifiManager.ConnectToSSID(item.Text, "");
        ActivityIndicator(true);
        //Now the timer should start.
        //And call PageDetail.ActivityIndicator(false) and warning the user if the timeout go to 0.
        listView.SelectedItem = null;
    }

}

I tried with the Timers Timer class but doesn't work.. any suggestion?


Solution

  • Ok I figured a solution! Instead of using Thread and Task, I used Device.StartTimer.

    In the event on the DetailPage I wrote:

    public void OnItemListClicked(object sender, SelectedItemChangedEventArgs e)
    {
        if (e.SelectedItem == null)
        {
            return;
        }
        ImageCell item = (ImageCell)e.SelectedItem;
        App.SelectedSSID = item.Text;
        App.WifiManager.ConnectToSSID(item.Text, "");
        ActivityIndicator(true);
        Device.StartTimer(TimeSpan.FromSeconds(10), () =>   //Waiting 10 second then if we are not connected fire the event.
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                if (App.IsLogout) //variable I use to check if I can do the logout or not
                {
                    ActivityIndicator(false);
                    App.SelectedSSID = "";
                    //My message to users "Fail to connect"
                }
            });
            return false;
        });
        listView.SelectedItem = null;
    }