-
Notifications
You must be signed in to change notification settings - Fork 0
Building Simple Chat Client with Parse
The following tutorial explains how to build a very simple chat application in Android using Parse backend-as-a-service.
Follow the registration guide to sign up for a parse account unless you are already registered.
Let's setup Parse into a brand new Android app following the steps below.
- Generate a new android project in your IDE (minSDK 14) and call it
SimpleChat
.- Name the first activity
ChatActivity
.
- Name the first activity
- Next, create an app in Parse and call it
SimpleChat
. Make note of theApplication ID
andClient Key
values after you have done so. - Follow the the steps mentioned under the setup guide to create and setup your project in eclipse.
- Drag the Parse jars into your
libs
folder - Create a class called
ChatApplication
which extends fromandroid.app.Application
- In the application, initialize parse with your application id and client key
- Drag the Parse jars into your
- Your application class should look like this after you have performed the above steps:
public class ChatApplication extends Application {
public static final String YOUR_APPLICATION_ID = "AERqqIXGvzH7Nmg45xa5T8zWRRjqT8UmbFQeeI";
public static final String YOUR_CLIENT_KEY = "8bXPznF5eSLWq0sY9gTUrEF5BJlia7ltmLQFRh";
@Override
public void onCreate() {
super.onCreate();
Parse.initialize(this, YOUR_APPLICATION_ID, YOUR_CLIENT_KEY);
}
}
- Make sure you have added these lines before the
<application>
tag in yourAndroidManifest.xml
.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- Also add the fully qualified name of your
Application
subclass to the<application>
tag in yourAndroidManifest.xml
.
<application
android:name="com.codepath.simplechat.ChatApplication"
...
<activity ... />
/>
Let's create an XML layout which allows us to post messages by typing into a text field. Open your layout file activity_chat.xml
, add an EditText
and a Button
to compose and send text messages.
<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"
tools:context="${relativePackage}.${activityClass}" >
<EditText
android:id="@+id/etMessage"
android:layout_toLeftOf="@+id/btSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:hint="@string/message_hint"
android:imeOptions="actionSend"/>
<Button
android:id="@+id/btSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|right"
android:paddingRight="10dp"
android:layout_alignParentRight="true"
android:text="@string/send"
android:textSize="18sp" >
</Button>
</RelativeLayout>
- Add the following values to
res-->values-->strings.xml
file.
<string name="message_hint">Say anything</string>
<string name="send">Send</string>
For the sake of simplicity, we will use an anonymous user to log into our simple chat app. An anonymous user is a user that can be created without a username and password but still has all of the same capabilities as any other ParseUser. After logging out, an anonymous user is abandoned, and its data is no longer accessible.
Open your main activity class (ChatActivity.java
) and make the following changes:
public class ChatActivity extends Activity {
private static final String TAG = ChatActivity.class.getName();
private static String sUserId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
// User login
if (ParseUser.getCurrentUser() != null) { // start with existing user
startWithCurrentUser();
} else { // If not logged in, login as a new anonymous user
login();
}
}
// Get the userId from the cached currentUser object
private void startWithCurrentUser() {
sUserId = ParseUser.getCurrentUser().getObjectId();
}
// Create an anonymous user using ParseAnonymousUtils and set sUserId
private void login() {
ParseAnonymousUtils.logIn(new LogInCallback() {
@Override
public void done(ParseUser user, ParseException e) {
if (e != null) {
Log.d(TAG, "Anonymous login failed.");
} else {
startWithCurrentUser();
}
}
});
}
}
Next, we will setup UI views in ChatActivity.java
. On click of 'Send' button, we'll save the message object to Parse. This is done by constructing a new ParseObject
and then calling saveInBackground()
to persist data to the database.
...
public static final String USER_ID_KEY = "userId";
private EditText etMessage;
private Button btSend;
...
// Get the userId from the cached currentUser object
private void startWithCurrentUser() {
sUserId = ParseUser.getCurrentUser().getObjectId();
setupMessagePosting();
}
private void setupMessagePosting() {
etMessage = (EditText) findViewById(R.id.etMessage);
btSend = (Button) findViewById(R.id.btSend);
btSend.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String data = etMessage.getText().toString();
ParseObject message = new ParseObject("Messages");
message.put(USER_ID_KEY, sUserId);
message.put("message", data);
message.saveInBackground(new SaveCallback() {
@Override
public void done(ParseException e) {
Toast.makeText(ChatActivity.this, "Successfully sent message to parse.",
Toast.LENGTH_SHORT).show();
}
});
etMessage.setText("");
}
});
}
At this point, run your application and try to send a text to parse. If the save was successful, you should see 'Successfully sent message to parse.' toast on your screen. To make sure the data was saved, you can look at the message
class in the Data Browser of your app on Parse.
- Now that we have verified that messages are successfully being saved to your parse database, lets go ahead and build the UI to retrive these messages. Open your layout file
activity_chat.xml
and add aListView
to display text messages from parse.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ffffff"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="@+id/lvChat"
android:transcriptMode="alwaysScroll"
android:layout_above="@+id/llSend"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<RelativeLayout
android:id="@+id/llSend"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:background="#ffffffff"
android:paddingTop="5dp"
android:paddingBottom="10dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:layout_height="wrap_content" >
<EditText
android:id="@+id/etMessage"
android:layout_toLeftOf="@+id/btSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:hint="@string/message_hint"
android:imeOptions="actionSend"/>
<Button
android:id="@+id/btSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|right"
android:paddingRight="10dp"
android:layout_alignParentRight="true"
android:text="@string/send"
android:textSize="18sp" >
</Button>
</RelativeLayout>
</RelativeLayout>
- We will be showing the logged in user's gravatar and messages on the right and the other gravatars and messages on the left. You can read more about creating gravatars here. We need to create another layout file for list view row. This is the main design component in this project as we define actual custom list design here. Name it
chat_item.xml
.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ImageView
android:id="@+id/ivProfileLeft"
android:contentDescription="@string/profile_other"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/ic_launcher" />
<TextView
android:textSize="18sp"
android:id="@+id/tvText"
android:padding="20dp"
android:lines="3"
android:layout_marginRight="64dp"
android:layout_width="match_parent"
android:layout_height="64dp">
</TextView>
<ImageView
android:id="@+id/ivProfileRight"
android:contentDescription="@string/profile_me"
android:layout_marginLeft="-64dp"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/ic_launcher" />
</LinearLayout>
- Add the following values to res-->values-->strings.xml file.
<string name="profile_me">My Profile Pic</string>
<string name="profile_other">Other profile pic</string>
Now create Message.java
class. This model class will be used to provide message data to ListView
.
public class Message {
public String userId;
public String text;
}
Create a class named ChatListAdapter.java
with below code. This is a custom list adapter class which provides data to list view. In other words it renders the layout_row.xml in list by pre-filling appropriate information. Also, download Picasso image library and drag it to the libs folder of your project.
public class ChatListAdapter extends ArrayAdapter<Message> {
private String mUserId;
public ChatListAdapter(Context context, String userId, List<Message> messages) {
this.mUserId = userId;
super(context, 0, messages);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.chat_item, parent, false);
final ViewHolder holder = new ViewHolder();
holder.imageLeft = (ImageView)convertView.findViewById(R.id.ivProfileLeft);
holder.imageRight = (ImageView)convertView.findViewById(R.id.ivProfileRight);
holder.text = (TextView)convertView.findViewById(R.id.tvText);
convertView.setTag(holder);
}
final Message message = (Message)getItem(position);
final ViewHolder holder = (ViewHolder)convertView.getTag();
final boolean isMe = message.userId.equals(mUserId);
// Show-hide image based on the logged-in user.
// Display the profile image to the right for the current user, left for all other users.
if (isMe) {
holder.imageRight.setVisibility(View.VISIBLE);
holder.imageLeft.setVisibility(View.GONE);
holder.text.setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
} else {
holder.imageLeft.setVisibility(View.VISIBLE);
holder.imageRight.setVisibility(View.GONE);
holder.text.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
}
final ImageView profileView = isMe ? holder.imageRight : holder.imageLeft;
Picasso.with(context).load(getProfileUrl(message.userId)).into(profileView);
holder.text.setText(message.text);
return convertView;
}
// Create a gravatar based on the hash value obtained from userId
private static String getProfileUrl(final String userId) {
String hex = "";
try {
final MessageDigest digest = MessageDigest.getInstance("MD5");
final byte[] hash = digest.digest(userId.getBytes());
final BigInteger bigInt = new BigInteger(hash);
hex = bigInt.abs().toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return "http://www.gravatar.com/avatar/" + hex + "?d=identicon";
}
final class ViewHolder {
public ImageView imageLeft;
public ImageView imageRight;
public TextView text;
}
}
Next, we will setup the ListView and bind our custom adapter to this ListView.
...
...
private ListView lvChat;
private ArrayList<Message> mMessages;
private ChatListAdapter mAdapter;
...
// Setup message field and posting
private void setupMessagePosting() {
etMessage = (EditText) findViewById(R.id.etMessage);
btSend = (Button) findViewById(R.id.btSend);
lvChat = (ListView) findViewById(R.id.lvChat);
mMessages = new ArrayList<Message>();
mAdapter = new ChatListAdapter(ChatActivity.this, sUserId, mMessages);
lvChat.setAdapter(mAdapter);
btSend.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String data = etMessage.getText().toString();
ParseObject message = new ParseObject("Messages");
message.put(USER_ID_KEY, sUserId);
message.put("message", data);
message.saveInBackground(new SaveCallback() {
@Override
public void done(ParseException e) {
receiveMessage();
}
});
etMessage.setText("");
}
});
}
Now we can fetch last 50 messages from parse and bind them to the ListView with the use of our custom messages adapter.
...
private static final int MAX_CHAT_MESSAGES_TO_SHOW = 50;
...
private void receiveMessage() {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Messages");
query.setLimit(MAX_CHAT_MESSAGES_TO_SHOW);
query.orderByDescending("createdAt");
query.findInBackground(new FindCallback<Message>() {
public void done(List<Message> messages, ParseException e) {
if (e == null) {
mMessages.clear();
mMessages.addAll(messages);
mAdapter.notifyDataSetChanged();
lvChat.invalidate();
} else {
Log.d("message", "Error: " + e.getMessage());
}
}
});
}
Finally, refresh the ListView with latest messages using a handler. The handler will call a runnable to fetch new messages every 100ms.
...
private Handler handler = new Handler();
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
if (ParseUser.getCurrentUser() != null) {
startWithCurrentUser();
} else {
login();
}
// Run the runnable every 100ms
handler.postDelayed(runnable, 100);
}
// Defines a runnable which is run every 100ms
private Runnable runnable = new Runnable() {
@Override
public void run() {
refreshMessages();
handler.postDelayed(this, 100);
}
};
private void refreshMessages() {
receiveMessage();
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codepath.simplechat"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name="com.codepath.simplechat.ChatApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".ChatActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Run your project and test it out with your pair partner. Below is the final output.
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.
Interested in ramping up on Android quickly?
(US Only) If you are an existing engineer with 2+ years of professional experience in software development and are serious about ramping up on Android quickly, be sure to apply for our free evening 8-week Android bootcamp.
We've trained over a thousand engineers from top companies including Apple, Twitter, Airbnb, Uber, and many others leveraging this program. The course is taught by Android experts from the industry and is specifically designed for existing engineers.
Not in the United States? Please fill out our application of interest form and we’ll notify you as classes become available in your area powered by local organizers.