Article 7 - Android Connectivity

http://www.codeproject.com/Articles/818734/Article-Android-Connectivity

Introduction

This article is dediated in explaining various Android Connectivity options. Below are the topics covered in this article.

Background

Please find the below link for "Introduction to Android"

http://www.codeproject.com/Articles/802449/Article-Introduction-to-Android
 

Please refer the below mentioned article to setup your development environment.

http://www.codeproject.com/Articles/803609/Article-Setting-Up-Your-Android-Development-Enviro

 

Basics of enabling connectivity in Android applications 


Let us try to understand some of the basic classes that are used as a part of the android network connectivity package.  

ConnectivityManager

The ConnectivityManager is the class which is used to monitor the status of the network connection for example WiFi, GPRS etc. This class is also responsible for handling some of the below mentioned things.

1.    Provides an API for querying the state of the available networks.
2.    Send broadcast intent wherever the network connectivity changes. 
3.    Switch network connectivity to other network when the connectivity gets lost. 

NetworkInfo

The NetworkInfo as the name itself indicates, it’s used to get the status of the network interface.  We will not be directly creating an instance of this class but use make a call to getActiveNetworkInfo() of ConnectivityManager which returns NetworkInfo instance. Then we can make a call to some of its methods like

isAvailable() – Returns true if the network connectivity is possible else this method returns false.

isConnected() – Returns true if the network connectivity exists and is possible to establish connection and pass data else this method returns false.

isRoaming() – Returns true if the device is currently roaming on the connected network else returns false.

isFailover() – Returns true if it’s a fail over. It generally happens when an attempt to connect the network through ConnectivityManager has resulted in fail over to this network as the other network connectivity was lost or disconnected.

BroadcastReceiver

The BroadcastReceviers are mainly used for registering and listening to system specific events for example application can register to get noticed whenever they receive an SMS received, battery status, know WiFI connection status, power connected or disconnected, storage changed etc. BroadcastReceiver is an abstract class, all you have to do is create a class which extends the BroadcastReceiver and implement the onReceive() method. Registering the broadcast receivers can be done within the AndoridManifest.xml or you can programmatically do the same.


HttpURLConnection

The HttpURLConnection extends itself from an URLConnection class which is mainly used for sending and receiving data over the web. Also it can be used for streaming data whose length is not known in advance. If you are dealing with https connection, then use HttpsURLConnection. 
Below is the code snippet to obtain an HttpURLConnection. First we will have to create an instance of URL by passing in the url. The openConnection method returns an instance of URLConnection. Based upon the protocol whether its ‘http’ or ‘https’ you can caste the respective class to obtain the instance of HttpURLConnection or HttpsURLConnection. 

URL urlConnection = new URL("http://www.codeproject.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

AndroidHttpClient

The AndroidHttpClient class is used to make remote HTTP Requests. In-order to create a new instance of this class, you can make a call to its static method, passing in a user agent. Note – The newInstance() method accepts null value too. By making a call to its getParams() method, you can get an instance of HttpParams were you can set some of the timeout properties before making an HTTP request. Note – Even if you do not set these timeout properties, when you are making a call to newInstance() method, by Default connection and socket timeout is set to 20 seconds. 

Below is the code block on how to use 

AndroidHttpClient client = AndroidHttpClient.newInstance(null);
HttpParams myParams = client.getParams();
HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
HttpConnectionParams.setSoTimeout(client.getParams(), 10000);

DefaultHttpClient

The DefaultHttpClient is also used to make remote HTTP requests. Please note that this class makes use of SingleClientConnManager for handling HTTP connections. Also one thing to always remember is, it’s not thread safe; which means access to an instance of a DefaultHttpClient from multiple threads can cause issues. It’s also known as Apache HTTP Client.

Here’s a code snippet on how to use DefaultHttpClient  to make an HTTP Request to ‘codeproject.com’ and get a HTTP Response by making a call to execute method, passing in the HttpGet request.

HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet("http://www.codeproject.com");
HttpResponse response = client.execute(request);

InputStream

The InputStream class is used to read data from a file system or network. It’s mostly used with BufferedInputStream, which basically wraps the InputStream and buffers the input. Here’s how you can make use of it. Note – Here we are making use of urlConnection instance and making a call to getInputStream, which returns an InputStream which you can use it to read data. 

InputStream in = new BufferedInputStream(urlConnection.getInputStream());

BufferedReader

The BufferedReader class is used with InputStream. The BufferedReader class basically wraps the input stream reader and buffers the input and then you can loop through and read text one line at a time. 
Here’s the code snippet on how to use BufferedReader. Here we are creating a new BufferedReader based on the inputStream and then use readLine() which returns a line of text. We will loop through until the readLine() method returns null.

BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((line = reader.readLine()) != null) {
      stringBuilderInstance.append(line);
}

Async Task

The Async task allows one to perform background operation and update results on the UI thread without having to manipulate thread of use handlers. The Async class is highly recommended to perform network operations which completes within few seconds. We will shortly see with an example how to use Async task to download data. 

Required Permissions for Network Connectivity

Here is the permission that one needs to use within the AndroidManifest.xml file so that the application can perform network connection. 

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 

Checking Connectivity

Now let us see how we can check for the network connectivity. It’s always a good practice to check whether the network connection is available, if so perform network operation. You will have to get an instance of NetworkInfo and then make a call to isConnected() method. 

Below is the code snippet you can use to perform the network connectivity check. Here’s what we do

1.    First gets an instance of ConnectivityManager by making a call to getSystemService passing in the CONNECTIVITY_SERVICE. 
2.    Get an instance of active NetworkInfo
3.    Check if the networkInfo instance is not null so that we can make a call to isConnected() method which returns true, if the connection is available. 

​public static boolean checkNetworkConnectivity(Context context) {

    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if (networkInfo != null) {
      return connectivityManager.getActiveNetworkInfo().isConnected();
    }
    return false;
  }

Performing a simple HTTP GET Request 

Now we will have a look into how to perform a simple HTTP Request. Here’s the code snippet for the same. 
Here’s what we do.

1.    Create an instance of HttpClient by using DefaultHttpClient
2.    Create an instance of HttpGet passing in the url to make a request. 
3.    Get the HttpResponse by making a call to execute method of http client instance. 
4.    Get an instance of HttpEntity from http response instance by making a call to getEntity() method. The HttpEntity instance holds the response body, and some of the attributes like content length, content type etc. 
5.    If the HttpEntity instance is not null, then get the content by making a call to getContent() which returns an InputStream instance.
6.    Using BufferedReader along with InputStreamReader and input stream, read the Http Response one line by making a call to readLine() method of BufferedReader instance. Just loop through until the readLine() method returns null.

HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://codeproject.com");

try {
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();

            if (httpEntity != null) {
                InputStream inputstream = httpEntity.getContent();
                BufferedReader bufferedReader =
                  new BufferedReader(new InputStreamReader(inputstream));
                StringBuilder responseStringbuilder = new StringBuilder();

                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    responseStringbuilder.append(line + "\n");
                }
 Log.v("HTTP RESPONSE", responseStringbuilder.toString());
                inputstream.close();
            }
} 
catch (Exception e) {
            // if any error, then print the stack trace.
            e.printStackTrace();
}

Connect and Download Data

Let us now see how we can use some of the basic classes mentioned above to connect and download the data. When it comes to downloading data, the time taken for the download to complete depends on the file size and the delays are always unpredictable. Here’s the recommendation, if you don’t really want to make your application to have a poor user experience, always perform any network operation in a different thread from the UI thread; the reason being, the application doesn’t hung up while the download happens. To be specific we will be using AsyncTask to perform asynchronous operations. 

Here’s the code snippet for the same. We will be downloading the CodeProject RSS feeds for latest articles. 

1.    Create a new project with a blank layout. 
2.    Add a button to the layout xml so that on click on that button, let us download the RSS Feed.
3.    Let us add the below mentioned permissions in AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

4.    Below is the code snippet for the activity onCreate override, were we reference the button and handle on click event.

final Button downloadDataButton = (Button) findViewById(R.id.btnDownload);
downloadDataButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                new DownloadWebPage().execute("http://www.codeproject.com/WebServices/ArticleRSS.aspx");
            }
});

In the above code, you will notice that we are making a call to execute method of DownloadWebPage passing in the URL to download the contents. Note - This method gets executed asynchronously.

Here’s the code snippet of DownloadWebPage custom class.

// Class with extends AsyncTask class
private class DownloadWebPage extends AsyncTask<String, Void, Void> {
        private String Response;
        private String Error = null;

        TextView textViewData = (TextView) findViewById(R.id.textViewData);

        protected void onPreExecute() {
            textViewData.setText("Downloading data, Please wait : ");
        }

        // Call after onPreExecute method
        protected Void doInBackground(String... urls) {
            HttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(urls[0]);

            try {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();

                if (httpEntity != null) {
                    InputStream inputstream = httpEntity.getContent();
                    BufferedReader bufferedReader =
                            new BufferedReader(new InputStreamReader(inputstream));
                    StringBuilder responseStringbuilder = new StringBuilder();

                    String line = null;
                    while ((line = bufferedReader.readLine()) != null) {
                        responseStringbuilder.append(line + "\n");
                    }
                    Response = responseStringbuilder.toString();
                    inputstream.close();
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        }

        protected void onPostExecute(Void unused) {
            if (Error != null) {
                textViewData.setText("Output : "+ Error);
            } else {
                textViewData.setText("Output : "+ Response);
            }
        }
}

About Async Task

Let us take a look into the method that gets invoked when you make a call to the ‘execute’ method of the Async Task.

1. The onPreExecute() method of Async class will be invoked on the UI thread before the task is executed. You can use this method to set the progress bar etc. It’s generally used for setting up of the task.
2. doInBackground(Params...) gets invoked in the background thread, just after the completion of onPreExecute() method. This is where you have to code the network operations, which might take some time to execute and the result of this method will be used to update the UI element. You can also use publishProgress(Progress...) method to publish the progress of the operation. 
3. onProgressUpdate(Progress...) gets called after a call to publishProgress(Progress…) 
4. onPostExecute(Result) gets invoked after when the background thread completes its operation. 

Note – The Async task can be canceled at any time by making a call to its cancel(boolean) method. One thing to remember, the Async tasks must be created only on the UI thread. 

Here’s the snapshot of the Async download sample application.

AsyncDownload

Checking the device network connection 

Now let us try to understand how to check the network connectivity of a device. Below is the code snippet you can make use off.

Here’s what we do.

1. Get an instance of ConnectionManager by making a call to getSystemService. 

2. Then we will be getting an instance of NetworkInfo based on the type – TYPE_WIFI or TYPE_MOBILE.

3. Once you get the NetworkInfo instance, you can make a call to its isConnected() method which returns true or false depending upon whether the connection is enabled or not.

ConnectivityManager connectionManager = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);

// Get an instance of NetworkInfo based on the type - WIFI 
NetworkInfo networkInfo = connectionManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 
boolean isWifiConnectionEnabled = networkInfo.isConnected();  

// Get an instance of NetworkInfo based on the type - MOBILE
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConnectionEnabled = networkInfo.isConnected();

Enable Application to pop up for network connectivity 

When it comes to applications that deal with network connectivity, you may have to let the users to select the WIFI or mobile network connection when there’s no connectivity exists. You can use the below code to show up an alert dialog through which the user can enable either mobile or Wifi network connection.

protected void EnableNetworkConnection() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("This application requires network connection. Please turn on mobile network or Wi-Fi in Settings.")
        .setTitle("Unable to connect to network")
        .setCancelable(false)
        .setPositiveButton("Settings",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                Intent wirelessSettingIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
                startActivity(wirelessSettingIntent);
            }
        }
    )
    .setNegativeButton("Cancel",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                MyActivity.this.finish();
            }
        }
    );
    AlertDialog alert = builder.create();
    alert.show();
} 

Do not forget to add the below permissions within your AndoridManifest.xml

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
Overview of different connectivity types and options
WiFi

Android has a dedicate package - android.net.wifi for managing wifi connections. The classes within this packages will communicate with the lower level stacks for providing wireless network access. 
When your application is dealing with wifi connectivity, the following are the permission that you may have to set within your AndoridManifest.xml file.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

Note – It’s recommended to set the following use-feature in your manifest.

<uses-feature android:name="android.hardware.wifi" />

Let us now discuss some of the classes that’s required for managing wifi connectivity. 

WifiManager

The WifiManager class is a primary API used for configuring, managing the Wifi network connections, scanning access points etc. You an instantiate WifiManager by using the below code

(WifiManager) getSystemService(Context.WIFI_SERVICE)

WifiInfo

The WifiInfo class contains the state of the wifi connection. The status of the connectivity may be either activity or is in the process of being setup. 
There are helpful methods to get the IP address, Mac Address, Network Id etc. 

ScanResult

The ScanResult class contains the information about the detected access point. You can get the following information from the ScanResult.

1.    BSSID – Specifies the address of the access point. It’s the Ethernet MAC Address in XX:XX:XX:XX:XX:XX format. Where each X is a hex digit.

2.    SSID – Specifies the network name. It’s an ASCII string. Ex: MyWifiNetwork or 02b443d406

3.    Frequency – Specifies the frequency in MHz with which the client is communicating with the access point.

4.    Level – Specifies the signal level in dBm.

WifiConfiguration

The WifiConfiguration class represents a configured wifi network. 

Check whether Wifi connectivity is available?

Let us take a look into the code and see how we can check whether the wifi connectivity is available or not. Here’s the code snippet for the same.

We are making use of ConnectivityManager and get an instance of NetworkInfo by making a call to getNetworkInfo with type as TYPE_WIFI. Once you get the network info instance, all you have to do is make a call to isConnected() method, which returns true if the wifi connectivity is available else it will return false.

public static boolean isWifiAvailable() {  
         ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
         if(connectivityManager == null) {  
             return false;  
         }  
         NetworkInfo networkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);  
         return networkInfo != null && networkInfo.isConnected();  
   }  

Scan Wifi access points

Let us now learn how to scan for wifi and list all wifi connectivity information in a ListView. We will be implementing a class which extends from a Broadcast receiver to get the scanned results and display the same in ListView. 

Here’s the code snippet for onCreate override. 

1. First we need to get the reference to our listview, later on with in the custom wifi broadcast receiver, we will be setting the ListView’s adapater to display all the wifi information. 
2. Next, we will be getting an instance of WifiManager by using getSystemService passing in the WIFI_SERVICE name.

3. Initiate a wifi scanning by making a call to startScan() method of WifiManager’s instance. 

4. Create a new instance of our custom broadcast receiver - WifiScanBroadCastReceiver

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wifi_scanner);

        // Get a reference to ListView element
        listViewWifi = (ListView) findViewById(R.id.listViewWifi);

        // Get an Instance of WifiManager so that we can Start scanning and list wifi connections
        wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        wifiManager.startScan();

        // Get an instance of our custom class wifiScanBroadCastReceiver so that we can register and un register
        wifiScanBroadCastReceiver = new WifiScanBroadCastReceiver();
}

Here’s the code snippet of WifiScanBroadCastReceiver class, which is used to get the scanned wifi access point information. All you have to do is, extent your custom class with BroadcastReceiver and implement onReceive() method. 

Notice the function call getScanResults() on WifiManager instance, which will return all the list of ScanResult, contains the available wifi access point information.

// Wifi Broadcast receiver class, the onReceive gets called
// when number of wifi connections changed
private class WifiScanBroadCastReceiver extends BroadcastReceiver {
      public void onReceive(Context c, Intent intent) {
            String wifiList[];
            List<ScanResult> wifiScanList = wifiManager.getScanResults();
            wifiList = new String[wifiScanList.size()];
            for(int i = 0; i < wifiScanList.size(); i++){
                String bssid =  wifiScanList.get(i).BSSID;
                String ssid = wifiScanList.get(i).SSID;
                int frequency = wifiScanList.get(i).frequency;
                wifiList[i] = String.format("BSSIS: %s, SSID: %s, Frequency: %s", bssid, ssid, String.valueOf(frequency));
            }

           listViewWifi.setAdapter(new ArrayAdapter<String>(getApplicationContext(),
                    android.R.layout.simple_list_item_1,wifiList));
        }
}

Here’s the code snippet for registering and unregistering the custom broadcast receiver with the intent filter set to SCAN_RESULTS_AVAILABLE_ACTION. 

You can use registerReceiver, unregisterReceiver to do the same.

// Register the WifiScanBroadCastReceiver
protected void onResume() {
        registerReceiver(wifiScanBroadCastReceiver, new IntentFilter(
                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
        super.onResume();
}

// Unregister the WifiScanBroadCastReceiver
protected void onPause() {
        unregisterReceiver(wifiScanBroadCastReceiver);
        super.onPause();
}

Here’s the snapshot of our WifiScanner demo app. 

wifiScanResult

When it comes to monitoring the wifi connections, it’s always good to go with broadcast receivers and you can keep track of the changes in the wifi connection and update the UI based upon the action that you specify one in Intent filter that you had used while registering the broadcast receiver. Here are the mostly commonly used ones. 

WIFI_STATE_CHANGED_ ACTION – This intent is broadcasted whenever there’s a change in the wifi hardware. The state can be either of the following: enabled, disabled, enabling, disabling, or unknown

WIFI_STATE_DISABLED – This intent is broadcasted when the Wi-Fi state is disabled.

WIFI_STATE_DISABLING - This intent is broadcasted when the Wi-Fi is currently being disabled.

WIFI_STATE_ENABLED - This intent is broadcasted when the Wi-Fi is enabled.

WIFI_STATE_ENABLING    - This intent is broadcasted when the Wi-Fi is currently being enabled.

WIFI_STATE_UNKNOWN - This intent is broadcasted when the Wi-Fi is in an unknown state. 

RSSI_CHANGED_ACTION - This intent is broadcasted when the signal strength has changed. 

SCAN_RESULTS_AVAILABLE_ACTION - This intent is broadcasted when the access point scan has completed and the results can be read within the broadcast class. 


Enabling and Disabling the Wifi Connection

Now let us see how we can enable or disable the wifi connection. Below is the code snippet for the same.

1.    First let us get a reference to the view Button elements for enabling and disabling wifi connection.
2.    Get an instance of WifiManager. 
3.    Handle button onClick events to enable and disable wifi connections. All you have to do is check whether wifi connection is available or not using isWifiEnabled() method and in addition to that, check for the wifi state then you can make a call to setWifiEnabled() method with true to enable and false to disable the wifi connection. 

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_enable_disable_wifi);

        // Get a reference to buttons
        btnTurnOnWifi = (Button)findViewById(R.id.btnTurnOnWifi);
        btnTurnOffWifi = (Button)findViewById(R.id.btnTurnOffWifi);

        // Get an Instance of WifiManager so that we can enable or disable wifi connection
        wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);

        // Handle onClick events
        btnTurnOnWifi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!wifiManager.isWifiEnabled() &&
                   wifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED ) {
                    wifiManager.setWifiEnabled(true);
                    Toast.makeText(getApplicationContext(), "Wifi enabled successfully!",
                            Toast.LENGTH_SHORT).show();
                }
                else
                    Toast.makeText(getApplicationContext(), "Wifi is already enabled",
                            Toast.LENGTH_SHORT).show();
            }
        });

        btnTurnOffWifi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(wifiManager.isWifiEnabled() &&
                   wifiManager.getWifiState() != WifiManager.WIFI_STATE_DISABLED ) {
                    wifiManager.setWifiEnabled(false);
                    Toast.makeText(getApplicationContext(), "Wifi disabled successfully!",
                            Toast.LENGTH_SHORT).show();
                }
                else
                    Toast.makeText(getApplicationContext(), "Wifi is already disabled",
                            Toast.LENGTH_SHORT).show();
            }
        });
}

EnableDisableWifi

Wifi Configuration

Let us now take a look into the code snippets which deals with WifiConfiguration class. 

Here’s the code snippet which deals with adding a wifi network.

WifiConfiguration wifiConfig = new WifiConfiguration(); 
wifiConfig.SSID = "\"".concat(ssid).concat("\"");
wifiConfig.status = WifiConfiguration.Status.DISABLED;
wifiConfig.priority = 40;

Below is the code snippet for network using WEP

wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); 
wifiConfig.wepKeys[0] = "\"".concat(password).concat("\"");
wifiConfig.wepTxKeyIndex = 0; 

Below is the code snippet for network using WPA and WPA2

wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 
wifiConfig.preSharedKey = "\"".concat(password).concat("\"");

Here’s the code snippet for adding a wifi network based on the above WifiConfiguration instance.

WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
int networkId = wifiManager.addNetwork(wifiConfig);
if (networkId != -1) {
   // do something
}

Peer to Peer communication using WI-FI Direct

The Wi-Fi P2P connection allows you to connect nearby devices by searching and connecting to them using sockets. It was introduced in Android 4.0 with API Level 18. The WI-FI direct, is much more reliable, faster and can be used for long ranges beyond the range of Bluetooth. Once the socket connection is established between the devices, you can either send or receive data as stream over the socket connection. You can make use of WI-FI direct connection and send data over the supporting devices like printers, scanners, televisions etc. 

Snapshot of the usage of WI-FI direct to communicate between a group containing one or many supporting devices. 

WI-FI-Direct

Image source - http://www.slideshare.net/EllingtonSu/wi-fi-direct

Here are some examples where you can make use of WI-FI direct.

1.    Send documents directly over the WI-FI direct supporting printers and print with easy.

2.    Using smart phones that support WI-FI direct can cast videos over the WI-FI direct supporting televisions. 

3.    Wirelessly transfer any kind of files between phones or other supporting devices; which is much faster than compared to Bluetooth technology. 

Some facts about Wi-Fi Direct

•    The Wi-Fi direct, does not use home, office or hotspot network. The peer to peer communication happens through socket connection. The data can be sent or received through the connected socket. 
•    Make use of WPA2 encryption protection to secure the data transfer.
•    Can transfer data at the speed of 2.5 to 3.0 Mbps. 
•    The Wi-Fi Direct can operate at up to 100m. There are reference site says it can support up to 656 feet.
•    The peer to peer group between the WI-FI direct supporting devices and communicating between all of them is also possible. 

The following are the methods of WifiP2pManager which is used to discover, connect between the peers.  

•    initialize()  - This method is used to register the application with the Wi-Fi framework. 
•    connect() – This method called to start a peer-to-peer connection with a device with the specified configuration.
•    cancelConnect() – This method is called to cancel any ongoing peer-to-peer group negotiation.
•    requestConnectInfo() – This method is used to request a device's connection information.
•    createGroup() – This method is used to create a peer-to-peer group with the current device as the group owner.
•    removeGroup()    - This method is used to remove the current peer-to-peer group.
•    requestGroupInfo() – This method is used to request peer-to-peer group information.
•    discoverPeers()    - This method used to initiate peer discovery.
•    requestPeers() – This method is used to request the current list of discovered peers.

Below are the permissions and minimum SDK support that’s required for peer to peer communication.  

<uses-sdk android:minSdkVersion="14" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Let us try to understand how W-FI direct works through sample code snippet. Below is the code block for a WI-FI direct broadcast receiver. I certainly don’t claim any of the WI-FI direct source code which I explained here as I have reused all from the google android sample. 

Here’s what happens.

1.    First we get the intent action based on the action; we will be deciding what to do.
2.    If the intent action is - WIFI_P2P_STATE_CHANGED_ACTION, we get the state and check if the peer to peer is enabled by verifying the state with WIFI_P2P_STATE_ENABLED. If it’s enabled, we will be enabling the WI-FI direct.
3.    If the intent action is - WIFI_P2P_PEERS_CHANGED_ACTION, we will be requesting for peers by making a call to requestPeers.
4.    If the intent action is - WIFI_P2P_CONNECTION_CHANGED_ACTION, get the network info and check if it’s connected, if so request connection information to get the group owner IP.
5.    If the intent action is - WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, then we will be updating the fragment accordingly. 

@Override
public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {

            // UI update to indicate wifi p2p status.
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                // Wifi Direct mode is enabled
                activity.setIsWifiP2pEnabled(true);
            } else {
                activity.setIsWifiP2pEnabled(false);
                activity.resetData();

            }
            Log.d(WiFiDirectActivity.TAG, "P2P state changed - " + state);
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

            // request available peers from the wifi p2p manager. This is an
            // asynchronous call and the calling activity is notified with a
            // callback on PeerListListener.onPeersAvailable()
            if (manager != null) {
                manager.requestPeers(channel, (PeerListListener) activity.getFragmentManager()
                        .findFragmentById(R.id.frag_list));
            }
            Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            if (manager == null) {
                return;
            }

            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if (networkInfo.isConnected()) {

                // we are connected with the other device, request connection
                // info to find group owner IP

                DeviceDetailFragment fragment = (DeviceDetailFragment) activity
                        .getFragmentManager().findFragmentById(R.id.frag_detail);
                manager.requestConnectionInfo(channel, fragment);
            } else {
                // It's a disconnect
                activity.resetData();
            }
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                    .findFragmentById(R.id.frag_list);
            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

        }
}

Now let us see how the file transfer works with WI-FI direct. The code makes use of a custom class FileTransferService which extends itself from IntentService. Underlying all we do is establishing socket connection based on the host address and port which comes along with the intent extras, get the input and outsteam so that we can read from the input stream and write into output stream.   

Note – The input stream is obtained by using ContentResolver and making a call to openInputStream with the file uri. The file path is coming from the intent extra (EXTRAS_FILE_PATH)

Also notice that the below IntentService logic makes a call to DeviceDetailFragment to copy a file by passing in the input and output streams. 

@Override
protected void onHandleIntent(Intent intent) {
        Context context = getApplicationContext();
        if (intent.getAction().equals(ACTION_SEND_FILE)) {
            String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);
            String host = intent.getExtras().getString(EXTRAS_ADDRESS);
            Socket socket = new Socket();
            int port = intent.getExtras().getInt(EXTRAS_PORT);

            try {
                Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");
                socket.bind(null);
                socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);

                Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());
                OutputStream stream = socket.getOutputStream();
                ContentResolver cr = context.getContentResolver();
                InputStream is = null;
                try {
                    is = cr.openInputStream(Uri.parse(fileUri));
                } catch (FileNotFoundException e) {
                    Log.d(WiFiDirectActivity.TAG, e.toString());
                }
                DeviceDetailFragment.copyFile(is, stream);
                Log.d(WiFiDirectActivity.TAG, "Client: Data written");
            } catch (IOException e) {
                Log.e(WiFiDirectActivity.TAG, e.getMessage());
            } finally {
                if (socket != null) {
                    if (socket.isConnected()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            // Give up
                            e.printStackTrace();
                        }
                    }
                }
            }

        }
}

Here’s the code block to copy a file. The function accepts the input and output stream, the program reads the input based on the buffer size and writes the same to output stream.

public static boolean copyFile(InputStream inputStream, OutputStream out) {
    byte buf[] = new byte[1024];
    int len;
    try {
        while ((len = inputStream.read(buf)) != -1) {
            out.write(buf, 0, len);
}
        out.close();
        inputStream.close();
    } catch (IOException e) {
        Log.d(WiFiDirectActivity.TAG, e.toString());
        return false;
    }
    return true;
}
Bluetooth

Android supports Bluetooth communication by providing a dedicated package “android.bluetooth” which contains classes helps for one to scan, discover, connect and wirelessly transfer data to nearby supported Bluetooth devices. 

When it comes to Bluetooth communication, two things to understand; if you wish to perform battery intensive operations, say you wish to stream and send data over Bluetooth then you can use classic Bluetooth communication. There are one another BLE – Bluetooth low energy (marketed as Bluetooth Smart) with a low power requirement which you can make use of it send small data with low power consumption. You can think of several use cases were you can make use of BLE’s to send data. Here are few examples: heart rate monitors can periodically send heart beat information, fitness devices can sync data so that apps can read and show graphs etc. Note – BLE was introduced in Android 4.3 (API Level 18)

Let us have a look into some of the commonly used classes of Bluetooth package.

BluetoothAdapter

The BluetoothAdapter represents the local Bluetooth adapter or the radio. It’s an entry-point for all Bluetooth interaction. You can use this class to

•    Discover nearby Bluetooth devices.
•    Query a list of paired devices.
•    Instantiate a BluetoothDevice using a known MAC address.
•    Create a BluetoothServerSocket to listen for communications from other devices.
BluetoothDevice
The BluetoothDevice represents a remote Bluetooth device. You can use this one to 
•    Request a connection with a remote device through a BluetoothSocket
•    Query information about the device such as its name, address, class, and pairing state etc. 

BluetoothSocket

The BluetoothSocket represents the interface for a Bluetooth socket; it’s quite similar to a TCP Socket. You can use this one as a connection point and transfer data with another Bluetooth device via InputStream and OutputStream.


BluetoothServerSocket

The BluetoothServerSocket represents an open server socket that listens for incoming requests which is similar to a TCP ServerSocket. Whenever you are making use of bluetooth in sending data, one of the devices must be opened as a server socket. The remote Bluetooth device makes a connection request to the device which opens a server socket. If everything goes well and the connection gets established, this class returns a BluetoothSocket through which you can send or receive data.

BluetoothClass

The BluetoothClass represents a general characteristics and capabilities of a Bluetooth device. It has read-only properties specify the device's major and minor device classes and its services. 

BluetoothProfile

The BluetoothProfile is an interface that represents a Bluetooth profile. The profiles are nothing but specifications for Bluetooth-based communication between devices. An example is the Hands-Free profile. There are additional profiles have been added as a part of BLE and those are called GATT-Based Specifications. Applications have to implement them and accomplish what they as supposed to do. 

BluetoothHeadset

Provides support for Bluetooth headsets to be used with mobile phones. This includes both Bluetooth Headset and Hands-Free (v1.5) profiles.

BluetoothA2dp

The A2DP is nothing but Advanced Audio Distribution Profile. You can use BluetoothA2dp for streaming of high quality audio from one device to another over a Bluetooth connection. 

BluetoothHealth

The BluetoothHealth class represents a Health Device Profile proxy that controls the Bluetooth service.
BluetoothHealthCallback.

The BluetoothHealthCallback is an abstract class that you use to implement BluetoothHealth callbacks. You must extend this class and implement the callback methods to receive updates about changes in the application’s registration state and Bluetooth channel state.

Application Permissions and Minimum SDK for Bluetooth Apps
The following are the minimum SDK and permission that an application requires for bluetooth communication. 

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
    
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

Let us take a look into a sample application to perform the following bluetooth operation.

1.    Turn on and Turn off Bluetooth.
2.    List paired devices.
3.    Search for new devices.

Here’s how the Bluetooth demo app looks like

BluetoothDemo

Here’s the code snippet of the activity class file for our demo Bluetooth App. All we are doing below is getting a reference to all the view elements say Button, Listview, ToggleButton etc. and setting up the onClick event handlers for each of the required view elements.

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bluetooth);

        // Get a reference to view elements
        toggleButton = (ToggleButton) findViewById(R.id.toggleButton);
        listView = (ListView) findViewById(R.id.listView1);
        showPairedDevicesButton =(Button) findViewById(R.id.pairedButton);
        findNearByDevicesButton =(Button) findViewById(R.id.searchButton);
        textInfo = (TextView)findViewById(R.id.textInfo);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if(bluetoothAdapter == null){
            toggleButton.setEnabled(false);
            findNearByDevicesButton.setEnabled(false);
            showPairedDevicesButton.setEnabled(false);
            textInfo.setText("Status: Device does not support Bluetooth");
            Toast.makeText(getApplicationContext(),"Sorry! This device does not support Bluetooth ",
                    Toast.LENGTH_LONG).show();
            return;
        }

        // Set the List Adapter
        BluetoothArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
        listView.setAdapter(BluetoothArrayAdapter);

        // Toggle Button Click
        // On State  - Call TurnOn method
        // Off State - Call TurnOff method
        toggleButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (toggleButton.isChecked() == false) {
                    TurnOn(v);
                } else {
                    TurnOff(v);
                }
            }
        });

        findNearByDevicesButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FindNearByDevices(v);
            }
        });

        showPairedDevicesButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PairedDevices(v);
            }
        });
}

Even though most of the android devices support Bluetooth, it’s always good to check whether the Bluetooth adapter or radio exists. If not, you can disable the view elements and show appropriate message.
Get an instance of BluetoothAdapter and check if it’s null, if so you have no luck which means device does not have a Bluetooth adapter. 

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter == null){
            toggleButton.setEnabled(false);
            searchButton.setEnabled(false);
            pairedButton.setEnabled(false);
            text.setText("Status: Device does not support Bluetooth");
            Toast.makeText(getApplicationContext(),"Sorry! This device does not support Bluetooth ",
                    Toast.LENGTH_LONG).show();
            return;
} 

Next let us see how we can turn on and turn off a bluetooth device. Below is the code snippet for the same.

public void TurnOn(View view){
        if (!bluetoothAdapter.isEnabled()) {
            Intent turnOnIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(turnOnIntent, 1);
        }
        else{
            Toast.makeText(getApplicationContext(),"Bluetooth is already on",
                    Toast.LENGTH_LONG).show();
        }
}

public void TurnOff(View view){
        bluetoothAdapter.disable();
        textInfo.setText("Status: Disconnected");

        Toast.makeText(getApplicationContext(),"Bluetooth turned off",
                Toast.LENGTH_LONG).show();
}

Here’s the snapshot of the application, shows the screen when the user clicks on the ‘Click To Turn On’ toggle button.

TurnOnBluetoothDevice

Let us take a look into how to find nearby Bluetooth devices. Below is the code snippet for the same.

Here’s what we do.

1.    When the user clicks on the ‘Find NearBy Devices’ button, the below function FindNearByDevices is executed. 
2.    We check whether the Bluetooth adapter is still discovering, if so we shall display a Toast message stating that ‘Bluetooth discovery in Progress!’ 
3.    Else, we will be initiating the Bluetooth discovery and registering a broadcast receiver with an intent filter - BluetoothDevice.ACTION_FOUND.
4.    Within the broadcast receiver, we look for the above intent filter action. If there’s a match, we will be getting a BluetoothDevice instance based on the intent by making a call to getParcelableExtra method of intent. 
5.    Once we get the BluetoothDevice instance, we will get the name and address and add the same to our bluetooth array adapter and notify dataset changed so that it gets displayed on the ListView element.

// Find nearby Bluetooth Devices
public void FindNearByDevices(View view) {
        if (bluetoothAdapter.isDiscovering()) {
            Toast.makeText(getApplicationContext(),"Bluetooth discovery in Progress!",
                    Toast.LENGTH_LONG).show();
        }
        else {
            BluetoothArrayAdapter.clear();
            bluetoothAdapter.startDiscovery();
            registerReceiver(bluetoothBroadCastReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
        }
}

final BroadcastReceiver bluetoothBroadCastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // Add the name and the MAC address
                BluetoothArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                BluetoothArrayAdapter.notifyDataSetChanged();
            }
}
};

Here’s the snapshot of the activity listing the Bluetooth devices when the user clicks on ‘Find NearBy Devices’ button.

FindNearByBluetoothDevices

Let us now take a look into how to list all the paired Bluetooth devices. Below is the code snippet for the same.

Here’s what we do.

1.    In order to get the paired devices, all you have to do is make a call to getBondedDevices method of BluetoothAdapter. That returns a set of BluetoothDevice. Please note that this method will only return those devices which are already paired. 

2.    Once you get a set of paired devices, all you have to do is, loop through each one of them and add into the Bluetooth array adapter with the device name and address.

// Get a list of paired devices
public void PairedDevices(View view){
        BluetoothArrayAdapter.clear();
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

        for(BluetoothDevice device : pairedDevices)
            BluetoothArrayAdapter.add(device.getName()+ "\n" + device.getAddress());

        Toast.makeText(getApplicationContext(),"List of Paired Bluetooth Devices",
                Toast.LENGTH_SHORT).show();
} 

Here’s the snapshot of the activity showing the paired Bluetooth devices.

PairedBluetoothDevices

Session Initiation Protocol (SIP)

Android provides full support for session initiation protocol (SIP), a full SIP stack has an integrated call management support with that you can make a fully featured telephony application to make and receive calls using SIP enabled android phones on both sides. Some of the real world scenarios were SIP is being used in video conferencing, instant messaging etc. In day to day life, you might be using voice call (audio or video) using your mobile, tablet or desktop; say just to name a few application – Skype, Viber etc. Knowingly or unknowingly you are using a technology called VOIP that is voice over IP which makes use of SIP. 

Note – The underlying technique how SIP works is by internally creating a socket connection with the remote host and all communication will happen over the opened socket. When the call gets hung, the socket connection gets closed. 

Let us walk through some of the limitations of SIP

1.    SIP requires data connection that means your phone or tablet should be having a mobile or WIFI data connection for it to work.

2.    Each participant who makes use of SIP for communication must have a valid SIP account. There are various providers who can offer SIP accounts.

3.    If you are running a mobile device, then the device must run Android 2.3 or higher. 

Let us take a look into some of the class 

•    SipAudioCall – The SipAudioCall is used to handle Internet audio call over SIP. It has methods to make, end, answer, hold and continue call. 

•    SipAudioCall.Listener – It’s the listener for events relating to a SIP call, such as when a call is being received ("on ringing") or a call is outgoing ("on calling").

•    SipErrorCode - The SipErrorCode defines the error codes returned during SIP actions.

•    SipManager - The SipManager provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related SIP services. You can do several things like initiate and receive calls, register and unregister SIP profile, create incoming call broadcast intent, create SIP session etc. 

•    SipProfile – The SpiProfile which is used to define a SIP profile. To create a SipProfile, you will have to use the Builder. Other ways of getting a SipProfile is by making a call to getLocalProfile() and getPeerProfile() of SipSession instance.

•    SipProfile.Builder – The Builder class which can be used for creating a SipProfile.

•    SipSession – The SipSession represents a SIP session that is associated with a SIP dialog or a standalone transaction not within a dialog. You can set a listener, initiate, answer, end or change call etc.

•    SipSession.Listener – It’s a listener for events relating to a SIP session which implements the below Listener interface. 

public interface Listener {
        void onReadyForCall(SipAudioCall call);
        void onRinging(SipAudioCall call, SipProfile caller);
        void onRingingBack(SipAudioCall call);
        void onCallEstablished(SipAudioCall call);
        void onCallEnded(SipAudioCall call);
        void onCallBusy(SipAudioCall call);
        void onCallHeld(SipAudioCall call);
        void onError(SipAudioCall call, Throwable e);
    }

•    SipSession.State – The State class defines the SIP session states. The state can be either of the following. 

o    READY_TO_CALL
o    REGISTERING
o    DEREGISTERING
o    INCOMING_CALL
o    INCOMING_CALL_ANSWERING
o    OUTGOING_CALL
o    OUTGOING_CALL_RING_BACK
o    OUTGOING_CALL_CANCELING
o    IN_CALL
o    PINGING
o    NOT_DEFINED

Below you will see bits and pieces of code snippets. Let us try to understand various aspects of SIP programming by digging into the Google Android SIP sample example – “WalkieTalkieActivity”. You can get the complete source from 

https://github.com/android/platform_development/tree/master/samples/SipDemo

Creating a SipManager
public SipManager manager = null; 
if (manager == null) {
manager = SipManager.newInstance(this);


Creating a SIPProfile


Let us now see how we can build a SipProfile and set various properties like username, password, port, protocol which will help in building a SipProfile.

SipProfile.Builder builder = 
        new SipProfile.Builder(username, domain);
builder.setPassword(password);
builder.setOutboundProxy(outboundProxy);
builder.setPort(port);
builder.setProtocol(protocol);
builder.setSendKeepAlive(true);
SipProfile sipProfile = builder.build();


Registering for In-coming SIP calls


Below is the code snippet, were we are creating an instance of Intent and set the action to receive incoming call. The SipManager will register with a SipRegistrationListener by making a call to setRegistrationListener method so that we can update the status.

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i,
             Intent.FILL_IN_DATA);
manager.open(sipProfile, pi, null);            
manager.setRegistrationListener(me.getUriString(),
    new SipRegistrationListener() {
            public void onRegistering(String localProfileUri) {
                updateStatus("Registering with SIP Server...");
            }

            public void onRegistrationDone(String localProfileUri,
                            long expiryTime) {
                updateStatus("Ready");
            }

            public void onRegistrationFailed(String localProfileUri, int errorCode,
                            String errorMessage) {
                updateStatus("Registration failed.  Please check settings.");
            }
});


Initiating an Audio SIP call


Here’s the code snippet for initiating a SIP call. Initiating a call requires creating a SipAudioCall listener instance that’s required for making an audio call. We will be using SipManager to initiate an audio call.  

In order to make a call, here’s what we need. SipProfile instance, SIP Address and a SipAudioCall Listener.

SipAudioCall.Listener listener = new SipAudioCall.Listener() {
    @Override
    public void onCallEstablished(SipAudioCall call) {
        call.startAudio();
        call.setSpeakerMode(true);                
        updateStatus(&ldquo;Call Established&rdquo;);
    }

    @Override
    public void onCallEnded(SipAudioCall call) {
        updateStatus("Call Ended");
    }
};

SipAudioCall call = manager.makeAudioCall(sipProfile.getUriString(), sipAddress,
        listener, 30);


Receive an Incoming SIP call 


In order to receive an incoming SIP call, we need to create a custom broadcast receiver by extending the BroadcastReceiver and implementing the onReceive() method to handle the incoming SIP calls.

We will have to create a new instance of SipAudioCall Listener and override the onRinging method stating that we will have to answer the call within the specified number of seconds in the answerCall method. In our case it’s 30 second. Note – This method is not responsible for answering the call. We will have to make use of SipManager and make a call to takeAudioCall method.

This broadcast listener makes use of the WalkieTalkieActivity which is an activity that’s built as a part of the SIP demo.  We will be using it in getting the SipManager instance so that we can take an audio call by making a call to takeAudioCall with the intent and the SipAudioCall Listener.

@Override
public void onReceive(Context context, Intent intent) {
    SipAudioCall incomingCall = null;
    try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
            @Override
            public void onRinging(SipAudioCall call, SipProfile caller) {
                try {
                    call.answerCall(30);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;

        incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
        incomingCall.answerCall(30);
        incomingCall.startAudio();
        incomingCall.setSpeakerMode(true);
        
if (incomingCall.isMuted()) {
            incomingCall.toggleMute();
        }

        wtActivity.call = incomingCall;
        wtActivity.updateStatus(incomingCall, 1);

    } catch (Exception e) {
        if (incomingCall != null) {
            incomingCall.close();
        }
    }
} 


Recommended Practice 


When your application makes use of SIP by any means, it’s always recommended to have some checks for VOIP support and make sure whether the device support Android SIP Stack or not. You can do that easily with the help of SipManager.

SipManager.isVoipSupported()
SipManager.isApiSupported()

USB 

Android provides classes which helps in communicating with the connected USB accessories. The android powered devices can be in USB Accessory mode or USB host mode. 

The below picture shows the difference between Accessory and Host mode; when the Android powered device is in host mode, it powers the bus were as with accessory mode, the USB device powers the android powered device. Note – When the android powered device is in USB accessory mode, the external device acts as a USB host. 

Here are examples of USB Accessory mode devices – Card Readers, Robotics Controllers, and Kiosks etc. Note - All USB accessory devices must adhere to the android USB communication protocol.  

usb-host-accessory

Image Source - http://developer.android.com/guide/topics/connectivity/usb/index.html

Below are the USB packages that you mostly have to import in-order to develop an application that communicates with the USB device.

import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest; 

UsbManager 

The UsbManager class is used for communicate with the connected USB devices. You can use this class to get the list of connected USB accessories. It has the following broadcast actions which you can make use of it within the broadcast receiver.

•    USB_CONNECTED – Indicates whether USB is connected or disconnected.
•    USB_CONFIGURATION – It’s a bundle containing name-value pairs where the name is the name of a USB function and the value 
o    USB_FUNCTION_ENABLED or USB_FUNCTION_DISABLED. 
•    ACTION_USB_ACCESSORY_ATTACHED – It’s a broadcast for USB accessory attached events. 
•    ACTION_USB_ACCESSORY_DETACHED – It’s a broadcast for USB accessory detached events. 


UsbDevice

The UsbDevice class represents a connected USB device and contains methods to access its identifying information, interfaces, and endpoints.

UsbInterface 

Represents an interface of a USB device, which defines a set of functionality for the device. A device can have one or more interfaces on which to communicate on.

UsbEndpoint 

The UsbEndpoint class represents an interface endpoint, which is a communication channel for this interface. An interface can have one or more endpoints, and usually has input and output endpoints for two-way communication with the device.

UsbDeviceConnection 

The UsbDeviceConnection class represents a connection to the device, you can use this class to transfers data on endpoints. You need this class to either send or receive data which can be synchronously or asynchronously.

UsbRequest 

The UsbRequest class represents an asynchronous request to communicate with a device through an UsbDeviceConnection.

UsbConstants 

Defines the USB constants. Please refer this link to see the full list of USB constants http://developer.android.com/reference/android/hardware/usb/UsbConstants.html


Searching and iterating through the USB Device List

Let us try to understand how we can get a list of connected USB devices. All we need is the UsbManager, we get an instance of the same by making a call to getSystemService with USB_SERVICE context.

Once we get a UsbManager, we need to make a call to getDeviceList() method, which returns a device list. Then you can use Iterator to iterate over the USB device list and get the device id, name etc. information.

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
  
  String usbDevice = "";
  while (deviceIterator.hasNext()) {
   UsbDevice device = deviceIterator.next();
   usbDevice += "\n" +
    "DeviceID: " + device.getDeviceId() + "\n" +
    "DeviceName: " + device.getDeviceName() + "\n" +
    "DeviceClass: " + device.getDeviceClass() + " - " 
     + translateDeviceClass(device.getDeviceClass()) + "\n" +
    "DeviceSubClass: " + device.getDeviceSubclass() + "\n" +
    "VendorID: " + device.getVendorId() + "\n" +
    "ProductID: " + device.getProductId() + "\n";
} 

Make sure to use minimum SDK and the uses feature.

<uses-feature android:name="android.hardware.usb.host" />
<uses-sdk
        android:minSdkVersion="12"
        android:targetSdkVersion="18" /> 


Building a USB Device Adapter


Let us now try to understand in building a simple USB device adapter to show a list of USB devices which are attached, unattached. We will be reusing the code snippets from https://code.google.com/p/googletv-android-samples/source

You can see below the custom UsbDeviceAdapter which is built by extending the BaseAdapter. This adapter can be used to set the Listview and update the list with all the connected USB devices. The key thing to understand is the BroadcastReceiver logic. 

The receiver gets the UsbDevice from intent and based upon the intent action, it will be either adding or removing USB device from the list of USB device array. Finally we will have to notify the dataset change so that the ListView gets updated accordingly. 

Within the getView() method, we will be inflating a list view, get all the view elements containing the TextView so that we can make a call to setText and set the vendor id, product id and device path.

private static class UsbDeviceAdapter extends BaseAdapter {
        private LayoutInflater mInflater;

        public UsbDeviceAdapter(Context context) {
            // Cache the LayoutInflate to avoid asking for a new one each time.
            mInflater = LayoutInflater.from(context);
        }

        public int getCount() {
            return mDevices.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = mInflater.inflate(R.layout.list_item, null);
            TextView vendorId = (TextView) convertView.findViewById(R.id.vendorId);
            TextView productId = (TextView) convertView.findViewById(R.id.productId);
            TextView devicePath = (TextView) convertView.findViewById(R.id.devicePath);
            final UsbDevice device = mDevices.get(position);
            vendorId.setText("Vendor Id: " + HEX_PREFIX + Integer.toHexString(
                    device.getVendorId()));
            productId.setText("Product Id: " + HEX_PREFIX + Integer.toHexString(
                    device.getProductId()));
            devicePath.setText("Device Path: " + device.getDeviceName());
            return convertView;
        }
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
            @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            UsbDevice usbDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            String deviceName = usbDevice.getDeviceName();

            synchronized (mDevices) {

                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                    if (usbDevice != null) {
                        mDevices.add(usbDevice);
                        mAdapter.notifyDataSetChanged();
                    } else {
                        Log.e(TAG, "USB device is not initialized");
                    }
                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                    if (usbDevice != null) {
                        mDevices.remove(usbDevice);
                        mAdapter.notifyDataSetChanged();
                    } else {
                        Log.e(TAG, "USB device is not initialized");
                    }
                }
            }
        }
};

Here’s the code snippet to register the BroadcastReceiver with the intent filter set with the below mentioned actions for device attached and un-attached. 

IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);

 
Requesting permission to communicate with the USB Device

Let us now see how you can request permission to the attached USB devices. Say we have already populated the ListView with all the attached USB devices; you can handle the permissions within the on click on ListView. Below is the code snippet or the same. 

It’s using a PendingIntent which you create an instance of the same within the activity onCreate override. The UsbManager is used in requesting the permission; you will have to make a call to requestPermission with the USB device and a pending intent. 

Note – The PendingIntent specifies an action to be taken in future. When you are giving a pending intent, you are the granting permission to UsbManager the right to perform the action that’s being specified in the pending intent.

private PendingIntent mPermissionIntent;

mPermissionIntent = PendingIntent.getBroadcast(
                this, 0, new Intent(ACTION_USB_PERMISSION), 0);

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        // request permission to communicate with the USB device.
        mUsbManager.requestPermission(mDevices.get(position), mPermissionIntent);

        // Show device details
        Intent intent = new Intent(UsbHostActivity.this, DeviceDetailsActivity.class);
        intent.putExtra(UsbManager.EXTRA_DEVICE, mDevices.get(position));
        startActivity(intent);
}  

Communicating with the USB Device

Let us now see how to communicate with the attached USB device. Let us go with a different approach to read and write data to an USB device. We will be making use of USB to Serial Driver which is located at https://github.com/mik3y/usb-serial-for-android and this is for Android 3.1+. 

In the above code, you have learnt how to get the attached UsbDevice. Now, it’s the time to learn how read and write data into your USB device using the above mentioned library. For reading the data you will have to make a call to read() method of UsbSerialPort instance and similarly if you wish to write a data, you can make a call to write() method. 

Here’s the code snippet you can use to read data from an USB device. 
1.    First thing we need to do is, get the list of available usb drivers by using UsbSerialProber with the UsbManager.
2.    Next, creating an UsbDeviceConnection based on the UsbManager and UsbDriver. 
3.    Then we will be getting ready to read data using UsbSerialPort, will open a connection and set the required Baud Rate (it’s the serial data speed) and read data into a buffer. 

// Find all available drivers from attached devices.
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
if (availableDrivers.isEmpty()) {
  return;
}

// Open a connection to the first available driver.
UsbSerialDriver driver = availableDrivers.get(0);
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
    return;
}

// Read some data! Most have just one port (port 0).
UsbSerialPort port = driver.getPort(0);
port.open(connection);
try {
  port.setBaudRate(115200);
  byte buffer[] = new byte[16];
  int numBytesRead = port.read(buffer, 1000);
  Log.d(TAG, "Read " + numBytesRead + " bytes.");
} catch (IOException e) {
  // Deal with error.
} finally {
  port.close();
}
Near Field Communication

Near field communication (NFC) as the name itself implies, the NFC’s are used for short range communication with a range of few centimeters. It’s designed to work with a close proximity range between the NFC supporting devices. 

The key thing behind the NFC is RFID (Radio frequency identification) is makes use of electromagnetic induction to transmit data. Unlike Bluetooth, the range of NFC is very short. In order for it to work, at least one of the devices must be a transmitting device and the other acts as a receiver to receive the signal. The NFC standard can be either active or passive and it depends on how the device works. 

When it comes to passive NFC device, it consists of information that other devices can read but the passing devices themselves don’t transmit any data. To take an analogy, you can think of it as QR reader tags, which holds information that your QR reader app can understand or read. 
The active NFC devices can read and send information and it does require power to do so. A device supporting active NFC can transmit data to nearby supported NFC devices. An example of active NFC device is public transport card readers or touch payment terminals were you can use your device to make payment. These can even alter the NFC tag information if the device is authorized to do. 


Although NFC does wireless data transfer, the way how it works is by making use of electromagnetic radio fields. But other wireless technologies like Bluetooth and WI-FI makes use of radio transmissions.  

Note – The maximum distance with which you can use NFC for communication is 10cm. One thing to remember is it’s only supported after Android 2.3.3.

Let us take a look into some of the classes that we will be using it for NFC communication. 

NfcManager

The NfcManager is a high level manager which can be used to obtain the device’s NfcAdapter. You can acquire an instance using getSystemService(String).

NfcAdapter

The NfcAdapter class represents the device's NFC adapter. It’s the entry-point for performing NFC operations. You can acquire an instance with getDefaultAdapter(), or getDefaultAdapter(android.content.Context).

NdefMessage

The NdefMessage class represents an NDEF data message, which is the standard format in which "records" carrying data are transmitted between devices and tags. 

NdefRecord

The NdefRecord class represents a record which is delivered in an NdefMessage. It basically describes the type of data being shared and carries the data itself. In an NDF Message, you will usually see one or more records.

Adding NFC Support in Android Application

In order for the application to make use of NFC, you will have to add the below mentioned details about the minimum SDK and uses feature indicating the usage of NFC. By specifying the NFC hardware, it lets the application to use NFC. 

<uses-sdk android:minSdkVersion="10"/>

<uses-feature android:name="android.hardware.nfc" android:required="true" />
 
<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />

Let us take a look into the NFC code and try to understand how we can program for NFC. 

Check if NFC is supported by your device

Below is the code snippet of the activity override to check whether the device supports NFC or not. Here’s what we do.

1. Get an instance of NfcAdapter by making a call to getDefaultAdapter of NfcAdapter.
2. If the above NfcAdapter instance is null, then the device does not support NFC.
3. Then we check whether the NFC is enabled or not by making a call to isEnabled() method of  NfcAdapter instance. Returns true, if the NFC is enabled else returns false.

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc);

        textView = (TextView) findViewById(R.id.textView);

        nfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if (nfcAdapter == null) {
            Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        if (nfcAdapter.isEnabled()) {
            textView.setText("NFC is enabled.");
        } else {
            textView.setText("NFC is disabled.");
        }
}

NFC Tag dispatching system

Assuming NFC is enabled, The Android powered devices (phone or tablet) will be usually looking for the NFC tags and this happens only when the screen is unlocked. In such cases when the device detects the NFC tag, the default behavior is to automatically choose the appropriate activity that can handle the intent. So you will have to code your application to handle all the NFC tags that you are interested in so that you don’t want to make the users to choose the application and handle the intent.  

Here’s what happens with the tag dispatching system, before it chooses an activity and delivers intent. The tag dispatching system analyses the scanned NFC tags, parses them to identify the MIME type or a URI within the payout which consists of NDEF formatted data. Further, it will encapsulate this information into the intent and it will start an activity based on the intent. This is how an application handling intent is can read the NDEF formatted data. 

Different type of NFC Tags

The NFC tag comes with several different technologies and can also have data written in many different ways. The tag dispatch system does job of understanding the NDEF message within the payload and map them to MIME or URI type. Technically speaking, the NDEF data will reside within the NdfMessage and it can have one or more records which are nothing but instances of NdfRecord. The tag dispatch system reads the first record NdfRecord to determine how to interpret the NDEF Message. 

Below are the fields with in the first NDEF record within a well-known formatted NDF Message. 

3-bit TNF (Type Name Format)

Specifies how to interpret the variable length type field. 

Variable length type – Specifies the type of the record. If using TNF_WELL_KNOWN, use this field to specify the Record Type Definition (RTD). 

Variable length ID – It’s a unique identifier for the record. This field is not used often, but if you need to uniquely identify a tag, you can create an ID for it.

Variable length payload – It’s the actual data payload that you want to read or write. An NDEF message can contain multiple NDEF records. 

The below tables are based on - http://developer.android.com/guide/topics/connectivity/nfc/nfc.html

Table 1. Supported TNFs and their mappings

Type Name Format (TNF)    Mapping

TNF_ABSOLUTE_URI        URI based on the type field.
TNF_EMPTY                     Falls back to ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE      URI based on the URN in the type field. The URN is encoded into the NDEF type field in a shortened form: <domain_name>:<service_name>. Android maps this to a URI in the form: vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA          MIME type based on the type field.
TNF_UNCHANGED          Invalid in the first record, so falls back to ACTION_TECH_DISCOVERED.
TNF_UNKNOWN             Falls back to ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN        MIME type or URI depending on the Record Type Definition (RTD), which you set in the type field. 

Table 2. Supported RTDs for TNF_WELL_KNOWN and their mappings

Record Type Definition (RTD)    Mapping

RTD_ALTERNATIVE_CARRIER    Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER       Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST      Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT          Falls back to ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER                URI based on parsing the payload.
RTD_TEXT                                  MIME type of text/plain.
RTD_URI                                    URI based on payload.

The above information regarding the TNF (Type Name Format) is being used by the tag dispatch system; and it will try to map the above mentioned MIME type or URI to the NDEF Message and if everything goes well, which means if the tag dispatch system clearly identifies the NDEF message with the MIME type, it will then encapsulate all these information into a dedicated ACTION_NDEF_DISCOVERED intent with the actual payload. This intent is what our application should be looking for to read NDF message. 

There are cases were the tag dispatch system won’t be able to correctly match on the NDF Message, in such cases all these information with the payload will be encapsulated to ACTION_TECH_DISCOVERED  intent. 

Dispatching of NFC tags to Application

Once the tag dispatching system maps the MIME type of URI to the NDEF Message and all these information with the payload is encapsulated within the intent and it will send the intent to the applications that filter these intents. Say if there is more than one application filtered the intent; the activity chooser will be show up and lets the user to select the activity for the intent to be used. Below are the intents that will be set by the tag dispatch system which is listed from highest to lowest order. 

ACTION_NDEF_DISCOVERED: This intent is used to start an Activity when a tag that contains an NDEF payload is scanned and is of a recognized type. This is the highest priority intent.

ACTION_TECH_DISCOVERED: if the tag that is scanned contains NDEF data that cannot be mapped to a MIME type or URI , then this intent will be set and the tag dispatch system will make a call to this intent. 

Say if none of the activities are registered to handle the ACTION_NDEF_DISCOVERED intent, the tag dispatch system tries to start an application with this intent.

ACTION_TAG_DISCOVERED: This intent is started if neither of the above intents are handled by the activities. 

The below picture depicts the intent is getting delivered to the activities based on how the tag dispatch system identifies the NDEF message and selects an appropriate intent.

nfc_tag_dispatch

Image source - http://developer.android.com/guide/topics/connectivity/nfc/nfc.html

Filtering of NFC Intents 

Let us now take a look into how we can apply intent filtering with in our application so that we make our application to handle only specific intents.

Mostly applications will filter the intent by ACTION_NDEF_DISCOVERED and use ACTION_TECH_DISCOVERED will be used as a fallback mechanism when the payload is not NDEF. However the last intent

ACTION_TAG_DISCOVERED is considered as a final resort when either of the discussed intents is matched. 
Here’s the code snippet you can use in Android ApplicationManifest.xml file to filter the intent based on the
ACTION_NDEF_DISCOVERED action.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="text/plain" />
</intent-filter>

If you are filtering the intents by ACTION_TECH_DISCOVERED with the ApplicationManifest.xml something like below, then you must create an XML resource file (let us name it as nfc_tech_filter.xml)  specifying the technologies that your activity supports within a tech-list set. 

<?xml version="1.0" encoding="utf-8"?>
<activity
    android:name="net.vrallev.android.nfc.demo.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
         
    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED" />
    </intent-filter>
 
    <meta-data
        android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />
</activity>

Here’s a template of the XML resource containing NFC tech-list. You can extend the list by adding whichever the one you are interested in handling.

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
</resources>

Below is the intent filtering for that you will have to code within the ApplicationManifest.xml for ACTION_TAG_DISCOVERED intent.

<intent-filter>
    <action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>


Reading data from NDEF tag

Now let us try to understand to read an NDEF tag data. We will be overriding the activity onNewIntent and make a call to handleIntent, passing in the intent received. 

@Override
protected void onNewIntent(Intent intent) {
    handleIntent(intent);
}

Here’s the logic of handleIntent function.

We are checking whether the intent action is ACTION_NDEF_DISCOVERED and also if the MIME type matches with the MIME_TEXT_PLAIN then we make a call to execute method of NdefReaderTask , which is an asynchronous task runs on background which is used to read text from NDF Message.

Here’s what we do in NdefReaderTask background async task

1.    Based on the Tag parameter, we are trying to get the NDEF instance. If it’s null, we just return null.

2.    Then we get the NdfMessage by making a call to the above Ndef instance method – getCachedNdefMessage()

3.    Next, we will be getting NDEF Records which is nothing but an array of NdefRecord instance.

4.    We will be looping through the NDEF records and make sure the TNF is TNF_WELL_KNOWN and NDEF Record type is RTD_TEXT, if so we make a call to read text from the payload.

5.    onPostExecute of our async task, will be called after the execution of doInBackground. Were we will be updating the result.

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {         
        String type = intent.getType();
        if (MIME_TEXT_PLAIN.equals(type)) {
 
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            new NdefReaderTask().execute(tag);
             
        } else {
            Log.d(TAG, "Wrong mime type: " + type);
        }
}

private class NdefReaderTask extends AsyncTask<Tag, Void, String> { 
    @Override
    protected String doInBackground(Tag... params) {
        Tag tag = params[0];
         
        Ndef ndef = Ndef.get(tag);
        if (ndef == null) {
            // NDEF is not supported by this Tag. 
            return null;
        }
 
        NdefMessage ndefMessage = ndef.getCachedNdefMessage(); 
        NdefRecord[] records = ndefMessage.getRecords();
        for (NdefRecord ndefRecord : records) {
            if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
                try {
                    return readText(ndefRecord);
                } catch (UnsupportedEncodingException e) {
                    Log.e(TAG, "Unsupported Encoding", e);
                }
            }
        } 
        return null;
    }
     
    private String readText(NdefRecord record) throws UnsupportedEncodingException {
        /*
         * See NFC forum specification for "Text Record Type Definition" at 3.2.1 
         * 
         * http://www.nfc-forum.org/specs/
         * 
         * bit_7 defines encoding
         * bit_6 reserved for future use, must be 0
         * bit_5..0 length of IANA language code
         */
 
        byte[] payload = record.getPayload();
 
        // Get the Text Encoding
        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
 
        // Get the Language Code
        int languageCodeLength = payload[0] & 0063;
         
        // String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
        // e.g. "en"
         
        // Get the Text
        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
    }
     
    @Override
    protected void onPostExecute(String result) {
        if (result != null) {
            mTextView.setText("Read content: " + result);
        }
    }
} 

References

http://developer.android.com/guide/topics/connectivity/index.html

The NFC explanation is based on the inspiration of

http://developer.android.com/guide/topics/connectivity/nfc/nfc.html

Reused sample code snippet for NFC and WI-FI direct from the below mentioned links

http://code.tutsplus.com/tutorials/reading-nfc-tags-with-android--mobile-17278

https://github.com/ahmontero/wifi-direct-demo/tree/master/src/com/example/android/wifidirect

Below is the link to the Bluetooth code which is modified to work effectively for our demo

http://examples.javacodegeeks.com/android/core/bluetooth/bluetoothadapter/android-bluetooth-example/

Points of Interest

I swear never though I would deep dive and go through most of connectivity aspects of Android. An endless exploration but I'm totally satisfied learning the topics discussed in this article.

Deep inside my heart, I had already become an expert with the connectivity options Smile | :)

History

Version 1.0 - Published the initial version of the article explaining various Android connectivity options - 09/14/2014.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值