Browse through more Android tutorials. If you'd like to see a tutorial on any particular topic, do leave a comment in the wishlist page. We frequently post new tutorials along with app releases. You may subscribe to our newsletter to get all updates in your inbox.
Now you can get the latest Java source bundled with each app update. Install the app from Google Play and go to Settings > Extras.

«  Create a reminder/alarm app Create Flappy Bird in Android  »

Create an Instant Messaging app using Google Cloud Messaging (GCM)

DownloadDownload

Keywords: Google Cloud Messaging Google Play services ContentProvider SQLiteOpenHelper BroadcastReceiver NotificationManager SimpleCursorAdapter CursorLoader ActionBar DialogFragment ListFragment ViewBinder ContentResolver PreferenceFragment Google App Engine Google Plugin JPA Servlet

Contents

2 « Prev Page

6. The Android Manifest file

The official documentation specifies few permissions required to use GCM for Android. Add the following lines to the manifest.





    <permission
        android:name="com.appsrox.instachat.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.appsrox.instachat.permission.C2D_MESSAGE" />
	
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
						
Notice that we create a new permission com.appsrox.instachat.permission.C2D_MESSAGE. Do not forget to change the package name if you use a different package for your application.
Next add a <receiver> to the <application> element to register a BroadcastReceiver to receive messages sent by the GCM Framework. Notice that we set the category as our application package in <intent-filter> for the receiver.
    <application
        android:name=".Common"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".client.GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.appsrox.instachat" />
            </intent-filter>
        </receiver>
    </application>
						
You may optionally create an intent service to handle the intents received by the broadcast receiver. However, we choose to handle the intent in the receiver itself for creating notifications. We'll implement the BroadcastReceiver later.

7. The Application class

This Application class gets instantiated first before all other components in the application. So its a good place to declare globals in an application although it's not mandatory. In manifest we specified the Application class by giving a value to android:name attribute of the <application> element.
To use it create a class that extends Application and override its onCreate() method.
	public class Common extends Application {	

		public static String[] email_arr;
		private static SharedPreferences prefs;

		@Override
		public void onCreate() {
			super.onCreate();
			prefs = PreferenceManager.getDefaultSharedPreferences(this);
			
			List<String> emailList = getEmailList();
			email_arr = emailList.toArray(new String[emailList.size()]);
		}
		
		private List<String> getEmailList() {
			List<String> lst = new ArrayList<String>();
			Account[] accounts = AccountManager.get(this).getAccounts();
			for (Account account : accounts) {
				if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
					lst.add(account.name);
				}
			}
			return lst;
		}	
	}
					
We add an utility method to get a list of all accounts on the device. This requires android.permission.GET_ACCOUNTS permission which we have already declared in the manifest. The user can choose one of these email addresses in the Settings screen to be used as the chat ID.
Later we'll add a few public getter methods to this class when we develop the Settings screen so that it's a common place to access application-wide preferences.

8. The Data model

The data model of the application comprises of two tables. The Profile table holds the user's name, chat email ID, and count of new messages. The Messages table holds all messages sent or received by the application along with time. Class Diagram Android provides SQLite database for data persistence. We'll next implement a ContentProvider to interact with the database.

9. The Content Provider

ContentProvider provides a standard interface to manage access to a structured set of data. It should be registered in the manifest just like any other application component.
	<provider
		android:name=".DataProvider"
		android:authorities="com.appsrox.instachat.provider"
		android:exported="false" >
	</provider>
						
Add the above snippet to the manifest and create a class DataProvider that extends ContentProvider. We'll have to implement few methods since ContentProvider is an abstract class.
public class DataProvider extends ContentProvider {

	public static final String COL_ID = "_id";
	
	public static final String TABLE_MESSAGES = "messages";
	public static final String COL_MSG = "msg";
	public static final String COL_FROM = "email";
	public static final String COL_TO = "email2";
	public static final String COL_AT = "at";
	
	public static final String TABLE_PROFILE = "profile";
	public static final String COL_NAME = "name";
	public static final String COL_EMAIL = "email";
	public static final String COL_COUNT = "count";					
					
	private DbHelper dbHelper;

	@Override
	public boolean onCreate() {
		dbHelper = new DbHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		return null;
	}

	@Override
	public String getType(Uri uri) {
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		return 0;
	}
}
					
We take help of SQLiteOpenHelper class provided by Android for managing the database. Create a nested DbHelper class that extends SQLiteOpenHelper.
	
	private static class DbHelper extends SQLiteOpenHelper {
		
		private static final String DATABASE_NAME = "instachat.db";
		private static final int DATABASE_VERSION = 1;

		public DbHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL("create table messages (_id integer primary key autoincrement, msg text, email text, email2 text, at datetime default current_timestamp);");
			db.execSQL("create table profile (_id integer primary key autoincrement, name text, email text unique, count integer default 0);");
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		}
	}
					
Add the following code to the DataProvider class.
	public static final Uri CONTENT_URI_MESSAGES = Uri.parse("content://com.appsrox.instachat.provider/messages");
	public static final Uri CONTENT_URI_PROFILE = Uri.parse("content://com.appsrox.instachat.provider/profile");
	
	private static final int MESSAGES_ALLROWS = 1;
	private static final int MESSAGES_SINGLE_ROW = 2;
	private static final int PROFILE_ALLROWS = 3;
	private static final int PROFILE_SINGLE_ROW = 4;
	
	private static final UriMatcher uriMatcher;
	static {
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcher.addURI("com.appsrox.instachat.provider", "messages", MESSAGES_ALLROWS);
		uriMatcher.addURI("com.appsrox.instachat.provider", "messages/#", MESSAGES_SINGLE_ROW);
		uriMatcher.addURI("com.appsrox.instachat.provider", "profile", PROFILE_ALLROWS);
		uriMatcher.addURI("com.appsrox.instachat.provider", "profile/#", PROFILE_SINGLE_ROW);
	}
					
Next, implement each overridden method of ContentProvider where we make use of UriMatcher to match the Uris passed to the methods.
	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		
		switch(uriMatcher.match(uri)) {
		case MESSAGES_ALLROWS:
		case PROFILE_ALLROWS:
			qb.setTables(getTableName(uri));
			break;
			
		case MESSAGES_SINGLE_ROW:
		case PROFILE_SINGLE_ROW:
			qb.setTables(getTableName(uri));
			qb.appendWhere("_id = " + uri.getLastPathSegment());
			break;
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);			
		}
		
		Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}
	
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		
		long id;
		switch(uriMatcher.match(uri)) {
		case MESSAGES_ALLROWS:
			id = db.insertOrThrow(TABLE_MESSAGES, null, values);
			if (values.get(COL_TO) == null) {
				db.execSQL("update profile set count=count+1 where email = ?", new Object[]{values.get(COL_FROM)});
				getContext().getContentResolver().notifyChange(CONTENT_URI_PROFILE, null);
			}
			break;
			
		case PROFILE_ALLROWS:
			id = db.insertOrThrow(TABLE_PROFILE, null, values);
			break;
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}
		
		Uri insertUri = ContentUris.withAppendedId(uri, id);
		getContext().getContentResolver().notifyChange(insertUri, null);
		return insertUri;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		
		int count;
		switch(uriMatcher.match(uri)) {
		case MESSAGES_ALLROWS:
		case PROFILE_ALLROWS:
			count = db.update(getTableName(uri), values, selection, selectionArgs);
			break;
			
		case MESSAGES_SINGLE_ROW:
		case PROFILE_SINGLE_ROW:
			count = db.update(getTableName(uri), values, "_id = ?", new String[]{uri.getLastPathSegment()});
			break;
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);			
		}
		
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
	
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		
		int count;
		switch(uriMatcher.match(uri)) {
		case MESSAGES_ALLROWS:
		case PROFILE_ALLROWS:
			count = db.delete(getTableName(uri), selection, selectionArgs);
			break;
			
		case MESSAGES_SINGLE_ROW:
		case PROFILE_SINGLE_ROW:
			count = db.delete(getTableName(uri), "_id = ?", new String[]{uri.getLastPathSegment()});
			break;
			
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);			
		}
		
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
	
	private String getTableName(Uri uri) {
		switch(uriMatcher.match(uri)) {
		case MESSAGES_ALLROWS:
		case MESSAGES_SINGLE_ROW:
			return TABLE_MESSAGES;
			
		case PROFILE_ALLROWS:
		case PROFILE_SINGLE_ROW:
			return TABLE_PROFILE;			
		}
		return null;
	}
					

At this point you may want to get the latest source code by installing the app from Google Play and extract it from the Settings screen.

Share the love:  

Next Page » 2

App Gen
App Name:
Project Name:
Package:
Screens:
Splash
Login
Help
Main
List  Grid  Pager
Detail
Settings
Options:
Action Bar
Navigation Drawer
Dummy Data
Generate
Free Apps