This tutorial is going to demonstrate step by step how to create a simple chat application on Android 2.2 and above, with instant messaging, online user detection and new message notification using Parse.com Backend as a service.
I.> Create a new project
Open Eclipse (with Android SDK environment set up) and create a new Android Project.
For persistence,
Application Name: ParseChat
ProjectName: ParseChat
Package Name: example.chat
Create a new blank activity. Let’s say LoginActivity.
Some versions of Eclipse would automatically generate the corresponding layouts for the main view and the menu. If not, you’ll have to create new xml files in the corresponding folders in /res
II.> Designing GUI View:
There are 3 views needing to be mentioned: Login, Register, FriendList, and the Chat View. Let’s call the respective Activities for them LoginActivity (already created), RegisterActivity, FriendListActivity and ChatActivity.
Layout for login activity:
—————-
<?xml version=”1.0″ encoding=”utf-8″?>
<ScrollView xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:fillViewport=”true” ><LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical”
android:padding=”10dip” ><TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/username”
android:textColor=”#372c24″ /><EditText
android:id=”@+id/login_email_txt”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginBottom=”20dip”
android:layout_marginTop=”5dip”
android:hint=”@string/username”
android:singleLine=”true” /><TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/password”
android:textColor=”#372c24″ /><EditText
android:id=”@+id/login_password_txt”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”5dip”
android:hint=”@string/password”
android:inputType=”textPassword”
android:singleLine=”true” /><Button
android:id=”@+id/btnLogin”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”10dip”
android:text=”@string/login” /><TextView
android:id=”@+id/link_to_register”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginBottom=”40dip”
android:layout_marginTop=”10dip”
android:gravity=”center”
android:text=”@string/register”
android:textSize=”18dip” />
</LinearLayout></ScrollView>
—————-
Layout for register activity
—————-
<?xml version=”1.0″ encoding=”utf-8″?>
<ScrollView xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:fillViewport=”true” ><LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical”
android:padding=”10dip” ><TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/username”
android:textColor=”#372c24″ /><EditText
android:id=”@+id/login_email_txt”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginBottom=”20dip”
android:layout_marginTop=”5dip”
android:hint=”@string/username”
android:inputType=”textEmailAddress”
android:singleLine=”true” /><TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/password”
android:textColor=”#372c24″ /><EditText
android:id=”@+id/login_password_txt”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”5dip”
android:hint=”@string/password”
android:inputType=”textPassword”
android:singleLine=”true” /><Button
android:id=”@+id/btnLogin”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”10dip”
android:text=”@string/login” /><TextView
android:id=”@+id/link_to_register”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_marginBottom=”40dip”
android:layout_marginTop=”10dip”
android:gravity=”center”
android:text=”@string/register”
android:textSize=”18dip” />
</LinearLayout></ScrollView>
—————-
Layout for Friend List
—————-
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent” ><ListView
android:id=”@+id/friendlist”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical” /></RelativeLayout>
—————-
Layout for Chat
—————-
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:orientation=”vertical” ><ScrollView
android:layout_width=”fill_parent”
android:layout_height=”0dip”
android:layout_weight=”1″ ><TextView
android:id=”@+id/chat_output_txt”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:paddingLeft=”5dp”
android:paddingRight=”5dp”
android:paddingTop=”5dp” />
</ScrollView><LinearLayout
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:baselineAligned=”true”
android:orientation=”horizontal”
android:paddingBottom=”5dp”
android:paddingLeft=”5dp”
android:paddingRight=”5dp” ><EditText
android:id=”@+id/chat_input_txt”
android:layout_width=”0dip”
android:layout_height=”60dip”
android:layout_weight=”1″
android:gravity=”top”
android:inputType=”textMultiLine”
android:scrollHorizontally=”false” ><requestFocus>
</requestFocus>
</EditText><Button
android:id=”@+id/chat_send_btn”
android:layout_width=”80dp”
android:layout_height=”45dp”
android:text=”@string/send” >
</Button>
</LinearLayout></LinearLayout>
—————-
III.> Permission for Parse
Parse will need some specific permissions in order to run. Add the following lines into your manifest files, right before the <application> tag.
<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED” />
<uses-permission android:name=”android.permission.VIBRATE” />
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
IV.> Adding a model into your Android application
1> Model Interface
The model Interface contains all the methods and functions which will be called by the activities.
—————-
public interface Model {
LinkedList<ParseUser> getUsers();
HashMap<String, String> getChatMessages();
String getChannel();
boolean isAuthenticated();
void logout();
void updateUserList();
void sendChatMessage(String userKey, String message);
void login(String username, String password) throws Exception;
void register(String username, String password, String confirm) throws Exception;
void updateUserTracking();
}
—————-
2.> Implementing the model interface
We are going to write a subclass of the Application class to persist data. This class will be implementing the Model interface.
Declaring some static variables and constants:
public class ChatApplication extends Application implements Model {
private static final String APP_ID = “LO6YE92er8izDGLEvBzHUFbO1VrD7a1tRFBp85Ih”;
private static final String CLIENT_KEY = “U1w2aGIoNbkwdGrw5jniGWB9ADBUejO3qtz4dR4l”;
private static LinkedList<ParseUser> users;
private static HashMap<String, String> chatMessages;
private static String channel = “”;
private static ParseUser user = null;
private static PendingIntent pIntent;
private static AlarmManager alarm;
}
Override onCreate()
@Override
public void onCreate() {
super.onCreate();
Parse.initialize(this, ChatApplication.APP_ID, ChatApplication.CLIENT_KEY);
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(true);
ParseACL.setDefaultACL(acl, true);
users = new LinkedList<ParseUser>();
chatMessages = new HashMap<String, String>();
Intent sIntent = new Intent(this, UpdateService.class);
pIntent = PendingIntent.getService(this, 0, sIntent, PendingIntent.FLAG_CANCEL_CURRENT);
alarm = (AlarmManager) getSystemService(ALARM_SERVICE);
logout();
startTracking();
}
Implement some simple functions
public LinkedList<ParseUser> getUsers() {
return users;
}public HashMap<String, String> getChatMessages() {
return chatMessages;
}public String getChannel() {
return channel;
}public boolean isAuthenticated() {
return (user != null && user.isAuthenticated());
}
Authentication with Parse
public void login(String username, String password) throws Exception {
username = username.trim().toLowerCase();
validateCredentialsFormat(username, password);
ParseUser.logIn(username, password);
startTracking();
}public void logout() {
if (user != null) {
ParseUser.logOut();
}
stopTracking();
}
Register a new user with Parse
public void register(String username, String password, String confirm) throws Exception {
username = username.trim().toLowerCase();
validateCredentialsFormat(username, password);
if (!password.equals(confirm)) {
throw new Exception(“Confirmation does not match”);
}
// Registration
ParseUser newUser = new ParseUser();
newUser.setUsername(username);
newUser.setPassword(password);
newUser.signUp();
startTracking();
}
Update the user list
public void updateUserList() {
ParseQuery query = ParseUser.getQuery();
// Only users with the last activities within 5 minutes
Date queryDate = new Date((new Date()).getTime() – 300000);
query.whereGreaterThan(“lastUpdate”, queryDate);
try {
List<ParseObject> list = query.find();
users.clear();
for (ParseObject o : list) {
users.add((ParseUser) o);
}
} catch (ParseException e) {
}
}
Send chat messages using Push Notification
public void sendChatMessage(String target, String message) {
try {
ParsePush push = new ParsePush();
push.setChannel(target);
push.setExpirationTimeInterval(86400);
push.setData(new JSONObject(“{” +
“”action”:”example.chat.MESSAGE”,” +
“”from”:”” + channel + “”,” +
“”to”:”” + target + “”,” +
“”message”:”” + message + “”}”));
push.sendInBackground();
} catch (JSONException e) {
}
}
Some private functions
private void validateCredentialsFormat(String username, String password) throws Exception {
// Some validations
if (username.length() * password.length() == 0) {
throw new Exception(“All fields must be filled”);
}
// The username should not be so complicated
if (!username.matches(“^([A-Za-z]+[A-Za-z0-9_]*)$”)) {
throw new Exception(“Username must start with a letter, and should only contain alphanumeric characters and underscores”);
}
}private void startTracking() {
stopTracking();
ParseUser usr = ParseUser.getCurrentUser();
if (usr != null && usr.isAuthenticated()) {
channel = usr.getUsername();
user = usr;
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 10000, pIntent);
}
}private void stopTracking() {
channel = “”;
user = null;
alarm.cancel(pIntent);
}public void updateUserTracking() {
if (user != null && user.isAuthenticated()) {
user.put(“lastUpdate”, new Date());
user.saveEventually();
}
}
3.> Auto update user’s states and auto refreshing
We have to update the current user’s information and retrieve the new information constantly (which I suggest every 4 minutes) so other users can retrieve the latest states of their friends. This is going to be achieved by calling updateUserTracking() function from the model using a service. An AlarmManager will be used for the timing. The timing is triggered in startTracking() and stopped in stopTracking()
public class UpdateService extends Service {
private Model model;
public UpdateService() {
model = (ChatApplication) getApplication();
}@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
model.updateUserList();
model.updateUserTracking();
}@Override
public IBinder onBind(Intent intent) {
return null;
}
}
V.> The activities
The activities in Android are like the controllers in MVC. The activities receives the inputs from the XML layout, use the implemented model to perform certain tasks corresponding with certain events.
Did you provide source code for this article. It would be helpful for me.Thanks