Transfer File Via Bluetooth in Android

Explains how to use the Android Bluetooth to use the Android Bluetooth APIs to accomplish the four major tasks necessary to communicate using Bluetooth.

Transfer File Via Bluetooth
What is the purpose of the Bluetooth?

Bluetooth is a wireless technology standard for exchanging data over short distances from fixed and mobile devices. The Android platform includes support for the Bluetooth network. The application framework provides access to the Bluetooth functionality through the Android Bluetooth APIs.These APIs let applications wirelessly connect to other Bluetooth devices.
 
Using the Bluetooth APIs, an Xamarin application can perform the following:
 
  • Scan for other Bluetooth devices
  • Query the local Bluetooth adapter for paired Bluetooth devices
  • Establish connection
  • Transfer data to and from other devices.
Bluetooth APIs is used to accomplish the four major tasks necessary to communicate using Bluetooth: setting up  your Bluetooth, finding devices that are either paired or available in the local area, connecting devices, and transferring data between devices.
 
How do I get started with the Bluetooth in Xamarin Android?
 
In order to use Bluetooth features in your application, you need to declare the Bluetooth permission BLUETOOTH to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data. and to initiate device discovery or manipulate Bluetooth settings, you need to declare the BLUETOOTH_ADMIN permission in addition to the BLUETOOTH permission.
Set the Bluetooth permission(s) in your Android application manifest file. For example:
<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  ...</manifest>

Here are the basic steps for working with a profile:-

Step 1 Get the default adapter.

protected override void OnCreate(Bundle bundle)
  {
     base.OnCreate(bundle);
     bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
     if (bluetoothAdapter == null)
      {
        Toast.MakeText(this, "Bluetooth is not available",ToastLength.Long).Show();
        Finish();
        return;
      }
}

Step 2- Setting Up Bluetooth

if (!BluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

Step 3 - Discovering Devices

protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            // Setup the window

            SetContentView(Resource.Layout.device_list);
            // Set result CANCELED incase the user backs out
            SetResult (Result.Canceled);

            // Initialize the button to perform device discovery            
            var scanButton = FindViewById<Button>(Resource.Id.button_scan);
            scanButton.Click += (sender, e) =>
            {
                DoDiscovery();
                (sender as View).Visibility = ViewStates.Gone;
            };

            // Initialize array adapters. One for already paired devices and
            // one for newly discovered devices
            pairedDevicesArrayAdapter = new ArrayAdapter<string> (this, Resource.Layout.device_name);
            newDevicesArrayAdapter = new ArrayAdapter<string> (this, Resource.Layout.device_name);
            
            // Find and set up the ListView for paired devices
            var pairedListView = FindViewById<ListView> (Resource.Id.paired_devices);
            pairedListView.Adapter = pairedDevicesArrayAdapter;
            pairedListView.ItemClick += DeviceListClick;
            
            // Find and set up the ListView for newly discovered devices
            var newDevicesListView = FindViewById<ListView> (Resource.Id.new_devices);
            newDevicesListView.Adapter = newDevicesArrayAdapter;
            newDevicesListView.ItemClick += DeviceListClick;
            
            // Register for broadcasts when a device is discovered
            receiver = new Receiver (this);
            var filter = new IntentFilter (BluetoothDevice.ActionFound);
            RegisterReceiver (receiver, filter);
            
            // Register for broadcasts when discovery has finished
            filter = new IntentFilter (BluetoothAdapter.ActionDiscoveryFinished);
            RegisterReceiver (receiver, filter);
            
            // Get the local Bluetooth adapter
            btAdapter = BluetoothAdapter.DefaultAdapter;
            
            // Get a set of currently paired devices
            var pairedDevices = btAdapter.BondedDevices;
            
            // If there are paired devices, add each one to the ArrayAdapter
            if (pairedDevices.Count > 0) {
                FindViewById<View> (Resource.Id.title_paired_devices).Visibility = ViewStates.Visible;
                foreach (var device in pairedDevices) {
                    pairedDevicesArrayAdapter.Add (device.Name + "\n" + device.Address);
                }
            } else {
                String noDevices = Resources.GetText (Resource.String.none_paired);
                pairedDevicesArrayAdapter.Add (noDevices);    
            }
            
        }

        protected override void OnDestroy ()
        {
            base.OnDestroy ();
            
            // Make sure we're not doing discovery anymore
            if (btAdapter != null) {
                btAdapter.CancelDiscovery();
            }
    
            // Unregister broadcast listeners
            UnregisterReceiver(receiver);
        }
        
        /// <summary>
        /// Start device discover with the BluetoothAdapter
        /// </summary>
        private void DoDiscovery ()
        {
            if (Debug)
                Log.Debug (TAG, "doDiscovery()");
            
            // Indicate scanning in the title
            SetProgressBarIndeterminateVisibility (true);
            SetTitle (Resource.String.scanning);
    
            // Turn on sub-title for new devices
            FindViewById<View> (Resource.Id.title_new_devices).Visibility = ViewStates.Visible;    

            // If we're already discovering, stop it
            if (btAdapter.IsDiscovering) {
                btAdapter.CancelDiscovery ();
            }
    
            // Request discover from BluetoothAdapter
            btAdapter.StartDiscovery ();
        }
        
    
        void DeviceListClick (object sender, AdapterView.ItemClickEventArgs e)
        {
            // Cancel discovery because it's costly and we're about to connect
            btAdapter.CancelDiscovery ();
            
            // Get the device MAC address, which is the last 17 chars in the View
            var info = (e.View as TextView).Text.ToString ();
            var address = info.Substring (info.Length - 17);
            
            // Create the result Intent and include the MAC address
            Intent intent = new Intent ();
            intent.PutExtra (EXTRA_DEVICE_ADDRESS, address);
            
            // Set result and finish this Activity
            SetResult (Result.Ok, intent);
            Finish ();
        }
        
        public class Receiver : BroadcastReceiver
        { 
            Activity _chat;

            public Receiver (Activity chat)
            {
                _chat = chat;
            }
            
            public override void OnReceive (Context context, Intent intent)
            { 
                string action = intent.Action;
                
                // When discovery finds a device
                if (action == BluetoothDevice.ActionFound) {
                    // Get the BluetoothDevice object from the Intent
                    BluetoothDevice device = (BluetoothDevice)intent.GetParcelableExtra (BluetoothDevice.ExtraDevice);
                    // If it's already paired, skip it, because it's been listed already
                    if (device.BondState != Bond.Bonded) {
                        newDevicesArrayAdapter.Add (device.Name + "\n" + device.Address);
                    }
                    // When discovery is finished, change the Activity title
                } else if (action == BluetoothAdapter.ActionDiscoveryFinished) {
                    _chat.SetProgressBarIndeterminateVisibility (false);
                    _chat.SetTitle (Resource.String.select_device);
                    if (newDevicesArrayAdapter.Count == 0) {
                        var noDevices = _chat.Resources.GetText (Resource.String.none_found).ToString ();
                        newDevicesArrayAdapter.Add (noDevices);
                    }
                }
            } 

Step 4- Connecting as a Client

To connect two devices, one must act as a server by holding an open BluetoothServerSocket. The purpose of the server socket is to listen for incoming connection requests and provide a connected BluetoothSocket after a request is accepted. Once the BluetoothSocket is acquired from the BluetoothServerSocket, the BluetoothServerSocket should be discarded.

protected class ConnectThread : Thread
        {
            private BluetoothSocket mmSocket;
            private BluetoothDevice mmDevice;
            private BluetoothChatService _service;

            public ConnectThread(BluetoothDevice device, BluetoothChatService service)
            {
                mmDevice = device;
                _service = service;
                BluetoothSocket tmp = null;

                // Get a BluetoothSocket for a connection with the
                // given BluetoothDevice
                try
                {
                    tmp = device.CreateRfcommSocketToServiceRecord(MY_UUID);
                }
                catch (Java.IO.IOException e)
                {
                    
                }
                mmSocket = tmp;
            }

            public override void Run()
            {
                Name = "ConnectThread";

                // Always cancel discovery because it will slow down a connection
                _service._adapter.CancelDiscovery();

                // Make a connection to the BluetoothSocket
                try
                {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    mmSocket.Connect();
                }
                catch (Java.IO.IOException e)
                {
                    _service.ConnectionFailed();
                    // Close the socket
                    try
                    {
                        mmSocket.Close();
                    }
                    catch (Java.IO.IOException e2)
                    {
                    }

                    // Start the service over to restart listening mode
                    _service.Start();
                    return;
                }

                // Reset the ConnectThread because we're done
                lock (this)
                {
                    _service.connectThread = null;
                }

                // Start the connected thread
                _service.Connected(mmSocket, mmDevice);
            }

            public void Cancel()
            {
                try
                {
                    mmSocket.Close();
                }
                catch (Java.IO.IOException e)
                {
                }
            }

Step 5- Connecting as a server

      private class AcceptThread : Thread
        {
            private BluetoothServerSocket mmServerSocket;
            private BluetoothChatService _service;

            public AcceptThread(BluetoothChatService service)
            {
                _service = service;
                BluetoothServerSocket tmp = null;

                // Create a new listening server socket
                try
                {
                    tmp = _service._adapter.ListenUsingRfcommWithServiceRecord(NAME, MY_UUID);

                }
                catch (Java.IO.IOException e)
                {
                    Log.Error(TAG, "listen() failed", e);
                }
                mmServerSocket = tmp;
            }

            public override async void Run()
            {
                Name = "AcceptThread";
                BluetoothSocket socket = null;
                
                    // Listen to the server socket if we're not connected
                    while (_service._state != BluetoothChatService.STATE_CONNECTED)
                    {
                        try
                        {
                            // This is a blocking call and will only return on a
                            // successful connection or an exception
                            socket = mmServerSocket.Accept();
                        }
                        catch (Java.IO.IOException e)
                        {

                            break;
                        }

                        // If a connection was accepted
                        if (socket != null)
                        {
                            lock (this)
                            {
                                switch (_service._state)
                                {
                                    case STATE_LISTEN:
                                    case STATE_CONNECTING:
                                        // Situation normal. Start the connected thread.
                                        _service.Connected(socket, socket.RemoteDevice);
                                        break;
                                    case STATE_NONE:
                                    case STATE_CONNECTED:
                                        // Either not ready or already connected. Terminate new socket.
                                        try
                                        {
                                            socket.Close();
                                        }
                                        catch (Java.IO.IOException e)
                                        {
                                          
                                        }
                                        break;
                                }
                            }
                        }
                    }
                
            }

            public void Cancel()
            {
                

                try
                {
                    mmServerSocket.Close();
                }
                catch (Java.IO.IOException e)
                {
                    Log.Error(TAG, "close() of server failed", e);
                }
            }
        }
 

 

Step 6 - Managing a Connection

 private class ConnectedThread : Thread
        {
            private BluetoothSocket mmSocket;
            private Stream mmInStream;
            private Stream mmOutStream;
            private BluetoothChatService _service;

            public ConnectedThread(BluetoothSocket socket, BluetoothChatService service)
            {
                Log.Debug(TAG, "create ConnectedThread: ");
                mmSocket = socket;
                _service = service;
                Stream tmpIn = null;
                Stream tmpOut = null;

                // Get the BluetoothSocket input and output streams
                try
                {
                    tmpIn = socket.InputStream;
                    tmpOut = socket.OutputStream;
                }
                catch (Java.IO.IOException e)
                {
                }

                mmInStream = tmpIn;
                mmOutStream = tmpOut;
            }
            byte[] memoryBuffer ;

            public override void Run()
            {
                int bytes;
                byte[] aa;
                var filePath = CreateNewImagePath();

                using (var ms = new MemoryStream())
                {
                    try
                    {
                        int i = 10;
                        while (i > 0)
                        {
                            bytes = mmInStream.Read(memoryBuffer, 0, memoryBuffer.Length);
                            ms.Write(memoryBuffer, 0, bytes);
                            aa = ms.ToArray();
                            if (i == 1)
                            {
                                Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
                                {
                                    var answer = await App.Current.MainPage.DisplayAlert("Bluetooth Transfer", "Do you wish to recieve the file", "Accept", "Reject");
                                    if (answer)
                                    {
                                        System.IO.File.WriteAllBytes(filePath, aa);
                                        Toast.MakeText(Application.Context, "File received", ToastLength.Short).Show();
                                    }
                                });
                            }
                            i--;
                        }
                    }
                    catch
                    {

                    }
                }
            }
            

            public void Write(byte[] buffer)
            {
                try
                {
                    mmOutStream.Write(buffer, 0, buffer.Length);
                    memoryBuffer = buffer;
                }
                catch (Java.IO.IOException e)
                {
                }
            }

            public void Cancel()
            {
                try
                {
                    mmSocket.Close();
                }
                catch (Java.IO.IOException e)
                {
                }
            }

 

Once the necessary stream has been acquired,the thread waits for data to come through the Input Stream. This method calls write(byte[]) to send the data to the remote device.