Friday, 1 January 2016

Bluetooth Communication

Bluetooth enables point to point & multipoint communication between bluetooth enabled devices. Android framework includes APIs for connection with other bluetooth enabled device and exchange data with connected device.

Android framework supports both Classic as well as Low Energy bluetooth and provides APIs for both. Classic bluetooth is used in app when both devices share a connection for longer periods & have large data to exchange. It drains the device battery being responsible for such intensive tasks. Low Energy bluetooth as name suggests consumes less power unlike classic bluetooth . It's application areas are very specific which suit intermittent burst data transfers. 

We will first cover classic bluetooth communication APIs and then low energy bluetooth communication.

In a point to point communication of bluetooth devices, one devices acts as a master & other as a slave. The master devices scans for available bluetooth enabled devices in vicinity. Slave devices is put in discoverable mode so that master can find it during scan. Once master device finds a device after scan, it initiates a connection. The connection is performed in 2 steps. First step is a pairing which generates a bond between two devices and confirms the identity of the devices. The pairing mechanism varies as per type of device & bluetooth version it supports. After a successful pairing between two devices, a connection is established through sockets. Let's dive into the code now.

For any bluetooth application you need to add these two permissions to your application's AndroidManifest.xml
 
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />

We will use the Android bluetooth API to connect to device using sockets. The communication will be over the socket streams.

For a master device, we will create the socket connection to the other bluetooth device. Then it will continuously listen for the data from the socket stream inside a thread. It can write to connected stream outside this thread. The connection is a blocking call and bluetooth device discovery being a heavy process, may slow down the connection. So it is a good practice to cancel the device discovery before trying to connect to other device. Note that the bluetooth socket connection is a blocking call and returns only if a connection is successful or if an exception occurs while connecting to device.

The BluetoothConnection will create the socket connection to other device, once instantiated and start listening to the data from connected device.

private class BluetoothConnection extends Thread {

 private final BluetoothSocket mmSocket;

 private final InputStream mmInStream;

 private final OutputStream mmOutStream;

 byte[] buffer;

 // Unique UUID for this application, you may use different
 private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");


 public BluetoothConnection(BluetoothDevice device) {

  BluetoothSocket tmp = null;

  // Get a BluetoothSocket for a connection with the given BluetoothDevice
  try {

   tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

  } catch (IOException e) {

   e.printStackTrace();

  }

  mmSocket = tmp;

  //now make the socket connection in separate thread to avoid FC

  Thread connectionThread  = new Thread(new Runnable() {

   @Override
   public void run() {

    // Always cancel discovery because it will slow down a connection
    mAdapter.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 (IOException e) {

     //connection to device failed so close the socket
     try {
      mmSocket.close();
     } catch (IOException e2) {
      e2.printStackTrace();
     }

    }
   }

  });

  connectionThread.start();

  InputStream tmpIn = null;
  OutputStream tmpOut = null;

  // Get the BluetoothSocket input and output streams
  try {

   tmpIn = socket.getInputStream();

   tmpOut = socket.getOutputStream();

   buffer = new byte[1024];

  } catch (IOException e) {

   e.printStackTrace();

  }

  mmInStream = tmpIn;
  mmOutStream = tmpOut;

 }



 public void run() {

  // Keep listening to the InputStream while connected
  while (true) {

   try {

    //read the data from socket stream
    mmInStream.read(buffer);

    // Send the obtained bytes to the UI Activity

   } catch (IOException e) {

    //an exception here marks connection loss
    //send message to UI Activity
    break;
   }
  }
 }



 public void write(byte[] buffer) {

  try {

   //write the data to socket stream
   mmOutStream.write(buffer);

  } catch (IOException e) {

   e.printStackTrace();

  }

 }

 public void cancel() {

  try {

   mmSocket.close();

  } catch (IOException e) {

   e.printStackTrace();

  }
 }

}

For a slave device, we need to put the device in discoverable mode so a master can connect to it. When the master device requests a connection, slave accepts the connection and streams to read from/write to are obtained from sockets.

For our device to be a slave Bluetooth device, put it in discoverable mode using following.

//Making the host device discoverable
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE),
      DISCOVERY_REQUEST_BLUETOOTH);
An instance BluetoothServerSocket listens for incoming requests upon discoverable mode is initiated successfully. This instance is obtained by calling the listenUsingRfcommWithServiceRecord method on the Bluetooth adapter. With this instance of BluetoothServerSocket, phone listens for incoming requests from remote devices through the start() method. Since listening is a blocking process we must use separate thread for the same.
 
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == DISCOVERY_REQUEST_BLUETOOTH) {
   boolean isDiscoverable = resultCode > 0;
   if (isDiscoverable) {
    UUID uuid = UUID.fromString(MY_UUID);
    String serverName = "BTserver";
    final BluetoothServerSocket bluetoothServer = 
      bluetoothAdapter.listenUsingRfcommWithServiceRecord(serverName, uuid);
    
    Thread listenThread = new Thread(new Runnable() {
    
     public void run() {
      try {
       
       BluetoothSocket serverSocket = bluetoothServer.accept();

      } catch (IOException e) {
      
       Log.d("BLUETOOTH", e.getMessage());
      }
     }
    });
    listenThread.start();
   }
  }
 }


Thursday, 4 July 2013

Writing Content Provider

Android provides a cleaner & simplistic approach to handle the structured data through SQLite databases. But ever wondered, if your application wants data to be shared with other applications?? First thought that strikes the mind is sharing the database file by keeping it at a common location, may be on SD card. This approach indeed can work for you. :) But wait, why not to give a second thought on this? What if user just wipes out all data from his SD card? Or what if just removes the card from phone & forgot to put it back? These are some examples of what might go wrong with the application. In both the cases, not only the application for which you shared but also, your own app will certainly loose all data and/or crash. Quite embarrassing...

Here, comes the elegant way of sharing your app data with other apps reliably. Content Providers!
Content provider is a wrapper over the database which allows other applications to access data generated by our app. The wrapper consists of the methods written for database's read, update & delete operations on SQLite database.

For custom content provider, we need to have the app database built up and we will be providing the wrapper over it for other applications. To make other apps aware that a content provider is available, declare it in AndroidManifest.xml as:

Here the name refers to the class MyContentProvider which extends ContentProvider class. You need to override following methods in this class.
  • onCreate() 
  • delete(Uri, String, String[]) 
  • getType(Uri)
  • insert(Uri, ContentValues) 
  • query(Uri, String[], String, String[], String) 
  • update(Uri, ContentValues, String, String[])
Usually these are wrapper functions for SQL queries on the sqlite database. You parse the input parameters and perform the queries on your database.
public class MyContentProvider extends ContentProvider {

 DatabaseHelper mDatabase;
        private static final int RECORDS = 1;
 public static final Uri CONTENT_URI = Uri
   .parse("content://com.example.android.contentprovider");

 public static final String AUTHORITY = "com.example.android.contentprovider";
 private static final UriMatcher matcher = new UriMatcher(
   UriMatcher.NO_MATCH);
 
 static {
  matcher.addURI(AUTHORITY, "records", RECORDS);
 }

 @Override
 public int delete(Uri uri, String selection, String[] selectionArgs) {
  // the app specific code for deleting records from database goes here
  return 0;
 }

 @Override
 public String getType(Uri uri) {
  int matchType = matcher.match(uri);
  switch (matchType) {
  case RECORDS:
   return ContentResolver.CURSOR_DIR_BASE_TYPE + "/records";
  default:
   throw new IllegalArgumentException("Unknown or Invalid URI " + uri);
  }
 }

 @Override
 public Uri insert(Uri uri, ContentValues values) {
                //your app specific insertion code goes here
                // it can be as simple as follows; inserting all values in database and returning the record id
  long id = mDatabase.getWritableDatabase().insert(Helper.TABLE_NAME,
    null, values);
  uri = Uri.withAppendedPath(uri, "/" + id);
  return uri;
 }

 @Override
 public boolean onCreate() {
                //initialize your database constructs
  return true;
 }

 @Override
 public Cursor query(Uri uri, String[] projection, String selection,
   String[] selectionArgs, String sortOrder) {
                //build your query with SQLiteQueryBuilder
  SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
  qBuilder.setTables(Helper.TABLE_NAME);
  int uriType = matcher.match(uri);
  
                //query the database and get result in cursor
  Cursor resultCursor = qBuilder.query(mDatabase.getWritableDatabase(),
    projection, selection, selectionArgs, null, null, sortOrder,
    null);
  resultCursor.setNotificationUri(getContext().getContentResolver(), uri);
  return resultCursor;

 }

 @Override
 public int update(Uri uri, ContentValues values, String selection,
   String[] selectionArgs) {
  // to be implemented
  return 0;
 }

}

By providing a content provider you avoid giving access to your database to other developers and also reduce the chances of database inconsistency.

Saturday, 9 February 2013

Integrating Google Analytics in Android App

As an enthusiastic developer, I always wish to offer many features in my application. Many times, it happens that a particular feature gets popular. I may be interested in finding such features which I would like keep enhancing. I may also cut down the features showing similarity to less popular features. But the question is how do I get such list? The common answer to this question would be taking feedback from users, having surveys, etc. How about getting this statistical data without all this hassle?

Yes, we can get the application usage data anonymously using Google Analytics. It works the same way, as we track the hit count for a particular web page. Using the statistics collected by Analytics, it is easy to plan the features for application.

Before we use Google Analytics in our app, we need an Analytics account and the Google Analytics SDK.

Download the Analytics SDK from here. Unzip the SDK and add libGoogleAnalytics.jar to your project's build path.

Add following permissions in your project's AndroidManifest.xml.

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

Now, sign into your analytics account and create a website profile for the App. The website URL can be fake but shall be descriptive enough. It is suggested that you use the reverse package name for this. For example, if the application package name is com.example.analytics.test; then the website URL for this app can be http://test.analytics.example.com. After successful creation of website profile, a web property ID is generated for that profile. Note it down as we will be using this in our app. This web property ID, also known as UA number of your tracking code, uniquely identifies the website profile. Note: You must mention in your App that you are collecting anonymous user data in your App to track your App.

After this setup, we are ready to track our application. Obtain the singleton instance of the tracker by calling GoogleAnalyticsTracker.getInstance() method. Then start tracking by calling its start()
method. Usually, we will want to track more than activities in the App. In such a scenario it is a good idea to have this tracker instance in onCreate() method of the Application class of the app.

public class TestApp extends Application {  
      
     //define your web property ID obtained after profile creation for the app   
      
      
     private String webId = "UA-XXXXXXXX-Y";  
      
      
     //Analytics tracker instance   
      
     GoogleAnalyticsTracker tracker;  
      
     @Override  
      
     public void onCreate() {  
      
      super.onCreate();   
      //get the singleton tracker instance  
      tracker = GoogleAnalyticsTracker.getInstance();  
      
      //start tracking app with your web property ID  
      tracker.start(webId,getApplicationContext());  
      
      //your app specific code goes here  
      
     }  
      
      
     /* This is getter for tracker instance.  
             This is called in activity to get reference to tracker instance. */  
      
     public GoogleAnalyticsTracker getTracker() {  
      
      return tracker;  
      
     }  
      
    }
You can track pageviews and events in the activity by calling trackPageView() and trackEvent() methods on tracker instance.
public class MainActivity extends Activity   
    {  
      
        @Override  
      
        protected void onCreate(Bundle savedInstanceState) {  
      
            super.onCreate(savedInstanceState);  
      
                  
      
            //track the page view for activity  
      
            GoogleAnalyticsTracker tracker = ((TestApp)getApplication()).getTracker();  
            tracker.trackPageView("/MainActivity");  
      
      
            /*You can track events like button clicks*/  
      
            findViewById(R.id.actionButton).setOnClickListener(  
                new View.OnClickListener() {           
      
            @Override  
            public void onClick(View v) {  
      
                  GoogleAnalyticsTracker tracker = ((TestApp)getApplication()).getTracker();  
                  tracker.trackEvent("Action Event","Action Button", "Button clicked",0);  
                  tracker.dispatch();  
      
            }  
      
             });  
      
            //your stuff goes here      
      
        }  
      
    }
Remember, your events and pageviews will not be sent to server until you call dispatch() method on tracker. In this way we can track all the activities and events inside them. By analyzing the various reports available on Google Analytics page, we can plan the features for the app.

Thursday, 20 September 2012

First Run Preferences

When designing the application, at times, we come across a situation, that we need certain things to be handled when user is using the app for the first time. It may be a Disclaimer note we are showing up for the usage  data collection of app. It, even, can be a simple "Thank You" for downloading our app. :-)

SharedPreferences comes handy in such scenarios. SharedPreferences is key-value pair based persistent storage type. We can use this to store the value which gets updated only once. Each time application launches it will check for this value in preferences. If the value is set, then this is not the first run of the application, else it is.

Application life cycle is managed with the Application class of Android framework. We are going to use SharedPreferences to store the value of first run. We will use a boolean variable to test if it is first run in preferences. When the application is installed and used for the first time, there are no preferences available for it. This time it will be created with provided default values. As we are testing for first run, we are going to use true as default value. In such case, the flag will return true value. After getting true flag, we will update this flag with false value as first run case has been executed and we no longer need flag to be true.



Here is the code snippet for the same:

public class MyApp extends Application {

 SharedPreferences mPrefs;

 @Override
 public void onCreate() {
  super.onCreate();

  Context mContext = this.getApplicationContext();
                //0 = mode private. only this app can read these preferences
  mPrefs = mContext.getSharedPreferences("myAppPrefs", 0);

 
  // the rest of your app initialization code goes here
 }
 
 public boolean getFirstRun() {
  return mPrefs.getBoolean("firstRun", true);
 }

 public void setRunned() {
  SharedPreferences.Editor edit = mPrefs.edit();
  edit.putBoolean("firstRun", false);
  edit.commit();
 }
  
}
This flag from preferences will be tested in launcher activity like this:

 if(((MyApp) getApplication()).getFirstRun()){
  //This is first run
  ((MyApp) getApplication()).setRunned();

  // your code for first run goes here

  }
 else{
  // this is the case other than first run
 }
 
When we publish an update to the app and the user downloads it via the Market, this will not modify the preferences, so the code works for only first run after installation. Consequent updates to the app will not bring the code into the picture, unless the user has manually uninstalled and installed the app. The preferences can also get modified, if user goes to Settings and clears data for the particular app. Once user clear application data, preferences get deleted. In such cases, above code will as if it is the first run of the application, even though it is not.
If you need a per-version mechanism you can, of course, just add a version number to the name used as a key in the shared preferences.

Similar technique can be used for distributing shareware versions of an Android app. i.e., we can limit the number of trials of the application using similar technique. In that case, we would use the integer count value in preferences for number of trials. Each trial would update the preferences. After the desired value is reached, we would block the usage of application.

Thursday, 6 September 2012

Android SQLite Tutorial



Many times, we come across the situation where we want to store the data permanently so that it doesn't get lost while app quits. For example, I have a simple application for playing some animals' sounds. I will need information of all animals, their sound names and the corresponding sound files. I may not hard code all these values in code and read each time the application loads. This may seem easier if I have a smaller set of data. Just imagine, how long the file size would grow if I have to add such 100 animals to my app. Even if we have copy paste to help, I am sure you may want to try something smarter.... ;-)

Android provides various ways for persistent data storage. They are:
  • Shared Preferences: These are key-value pairs stored in the device's internal memory within the application space. These may be shared across the applications as needed with proper permissions.
  • Internal files: These are similar to various types of files which are stored into internal memory on phone within application space. These are useful for some configuration files or some secured data files. 
  • SD Card Storage: These are same as the internal files except the fact that these are actually stored on the external SD card of the device and accessible to users.
  • SQLite Databases: These are lightweight databases used for storing structural data.These are helpful when we need to store the data in forms of records and need to access it efficiently.
Let's now start with how to use SQLite database in our application. Consider above scenario. We need to store data for each animal like animal name, its sound, animal graphics resource and sound file location. We will have a table with the fields in it as animal_name, sound_name, graphics_id and sound_file. This is going to be our database schema.

But how am I going to add this to my application? Android provides us with SQLiteOpenHelper class for managing our SQLite databases. This class provides us with methods for creating and upgrading databases. Let's start with database creation first.

Let's have an DatabaseHelper class, inheriting SQLiteOpenHelper, for handling all database operations. When database needs to be created, onCreate() method is called by operating system. We will add our SQL statements for populating the database in this onCreate() method.

We are also going to add a method for fetching all animals records from database. Let's name it as getAllAnimals(). This will return a cursor for all database records.

public class DatabaseHelper extends SQLiteOpenHelper {

 private static final int DB_VERSION = 1;
 private static final String DB_NAME = "my_contacts.db";

 //Constants for database table names
 private static final String ANIMALS = "animals";
 
 //Constants for table fields
 private static final String ANIMAL_NAME = "animal_name";
 private static final String SOUND_NAME = "sound_name";
 private static final String GRAPHICS_ID = "graphics_id";
 private static final String SOUND_FILE = "sound_file";
 

 public DatabaseHelper(Context context) {
  super(context, DB_NAME, null, DB_VERSION);
 }

 @Override
 public void onCreate(SQLiteDatabase database) {
  //This method will be called when database needs to be created.
  //Use this to create initial setup such as creating tables.
  String sql = "create table "+ ANIMALS + "(_id integer primary key autoincrement, " +
    ANIMAL_NAME +" text," +
    SOUND_NAME+" text," +
    GRAPHICS_ID + " integer," +
    SOUND_FILE + " text)";
  database.execSQL(sql);
  
  //Our code for populating animals database goes here. 
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  //This method may not be used every time. 
  //It comes handy when you want to update database during app upgrades.
 }
 
 /**
  * This method is for fetching all animals data from database.
  * @return
  */
 Cursor getAllAnimals(){
  SQLiteDatabase db = getReadableDatabase();
  Cursor cursor = db.query(ANIMALS, null, null, null, null, null, null);
  return cursor;
 }