16
Oct
2015

Simple Chat | Parse.com | Anonymous

Dalam era digital saat ini, komunikasi dengan orang lain sangat-sangatlah mudah. Banyak sekali media sosial yang telah menyediakan fiturnya. Salah satu fitur yang populer adalah chat. Dengan chat, kita dapat real time berbincang-bincang dengan orang lain. Gimana kalau kita buat aplikasi chat kita sendiri, seru kan? Let’s make it!

Parse telah ditutup pada tanggal 28 Januari 2017. Tutorial dibawah ini tidak akan bisa berjalan.

Parse closed articleBlog Parse

Untuk tutorial kali ini kita akan menggunakan Parse.com sebagai backendnya. Jadi tidak perlu ribet-ribet membuat file php dan database. Semuanya tinggal coding diandroidnya saja. Lalu untuk networking librarynya, kita gunakan bolts, bolts ini sepaket dengan parse.

 

Jika ingin mencobanya terlebih dahulu, silahkan download apknya:

[maxbutton id=”1″ url=”http://www.andevindo.com/?smd_process_download=1&download_id=672″]

 

Langkah-langkah yang akan kita lakukan dalam membuat simple chat sebagai berikut:

  1. Mengatur bagian parse.com (Account, new App, setting)
  2. Menyiapkan library yang diperlukan
  3. Mendisain tampilan
  4. Coding time

 

Mengatur Parse.com

Mari kita buka terlebih dahulu https://www.parse.com. Setelah itu login atau jika belum punya akun, tentunya registrasi terlebih dahulu. Setelah semua masalah account selesai, masuk ke bagian dashboard atau masuk ke https://www.parse.com/apps.

1

 

  1. Klik Create a new App, beri nama dan klik button Create.
  2. Pilih App yang telah dibuat dan masuk ke setting.
    2
  3. Masuk ke bagian menu Keys. Kita akan gunakan Application ID dan Client Key diaplikasi android.
    3

 

Library yang dibutuhkan

Seperti biasa, untuk mensuport versi lama dari android saya gunakan appcompat.

compile 'com.android.support:appcompat-v7:23.0.1'

Untuk tampilan, dibutuhkan library picasso untuk menampilkan image dari url. Selain itu dibutuhkan juga library recyclerview untuk menampilkan pesan yang dikirimkan.

compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.android.support:recyclerview-v7:23.0.1'

Untuk library parsenya, harus download filenya terlebih dahulu. Silahkan buka https://parse.com/docs/downloads, pilih android sdk. Setelah terdownload, extract hasil download tadi, dan copy file Parse-x.x.x.jar ke dalam folder libs pada project. x.x.x tersebut adalah versi dari parse, untuk projek ini saya gunakan versi 1.10.2.

4

Lalu tambahkan berikut digradle:

compile fileTree(dir: 'libs', include: 'Parse-1.10.2.jar')
compile 'com.parse.bolts:bolts-android:1.+'

Keseluruhan akan terlihat seperti ini:

5

Sync project, pastikan tidak ada error ketika sync, karena jika error, maka library tidak bisa digunakan.

 

Mendisain Tampilan

Tampilan untuk simple chat ini cukup simple, sesuai dengan namanya ya. Hanya butuh satu activity saja, didalamnya terdapat recycler view untuk menampilkan pesan yang telah dikirim. Lalu ada edittext dan button, untuk input pesan dan kirim pesan. Adapter untuk recyclerview disini dibuat custom, karena akan kita buat sendiri juga tampilan itemnya.

activity_main.xml (Design)

6

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/container"
        ></android.support.v7.widget.RecyclerView>

    <LinearLayout
        android:id="@+id/container"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <EditText
            android:id="@+id/message"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:imeOptions="actionSend"
            android:hint="Say something..."/>
        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send"/>
    </LinearLayout>
</RelativeLayout>
custom_chat_layout.xml (Design)

7

custom_chat_layout.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="wrap_content"
    android:orientation="horizontal"
    android:padding="2dp"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/gravatarLeft"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="5dp"
        android:src="@mipmap/ic_launcher" />

    <RelativeLayout
        android:id="@+id/container"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        >

        <TextView
            android:id="@+id/message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:text="Message"
            android:textColor="@android:color/white"
            android:layout_alignParentRight="true"
            android:padding="6dp"/>


    </RelativeLayout>

    <ImageView
        android:id="@+id/gravatarRight"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="5dp"
        android:src="@mipmap/ic_launcher" />


</LinearLayout>

 

Coding time

 

Berikut adalah screenshot projek saya untuk tutorial kali ini:

8

 

Berikut adalah alur jalannya aplikasi:

9

Gambar diatas adalah alur jalannya aplikasi yang berhubungung dengan UI pada tampilan. Sebelum proses diatas dijalankan, perlu dilakukan inisialisasi parse terlebih dahulu, hal ini dilakukan di MyApplication.class. Fungsi dari inisialisasi ini adalah mendaftarkan device di parse.com.

10

Setelah inisialisasi selesai, lalu aplikasi akan mengecek userId dilokal, jika tersedia, maka akan menggunakan userId tersebut, namun jika tidak tersedia, maka akan mendaftarkan anonymous userId di parse.com. Setelah userId didapatkan, lalu alur jalannya aplikasi bisa dilihat dialur simple chat diatas.

 

Package Activity

MainActivity.java
package heends.simplechat.Activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.parse.FindCallback;
import com.parse.LogInCallback;
import com.parse.ParseAnonymousUtils;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParsePush;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import com.parse.SaveCallback;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import heends.simplechat.Adapter.ChatAdapter;
import heends.simplechat.ParseObject.Message;
import heends.simplechat.R;
import heends.simplechat.Utils.ObservableObject;

public class MainActivity extends AppCompatActivity implements Observer{

    private RecyclerView mRecycler;
    private Button mSend;
    private EditText mMessage;
    private List<Message> mList;
    private ChatAdapter mAdapter;
    private String mUserId;
    private LinearLayoutManager mManager;
    private boolean mFirstLoad = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ObservableObject.getInstance().addObserver(this);

        if (ParseUser.getCurrentUser() != null) {
            startWithCurrentUser();
        } else {
            login();
        }
        setupMessage();
        receiveMessages();
    }

    private void startWithCurrentUser() {
        mUserId = ParseUser.getCurrentUser().getObjectId();
        setupMessage();
    }

    private void login() {
        ParseAnonymousUtils.logIn(new LogInCallback() {
            @Override
            public void done(ParseUser user, ParseException e) {
                if (e != null) {
                    Log.d("Error", "Anonymous login failed: " + e.toString());
                } else {
                    startWithCurrentUser();
                }
            }
        });
    }


    //Inisialisasi variable dan listener untuk button send
    private void setupMessage() {
        mSend = (Button)findViewById(R.id.send);
        mMessage = (EditText)findViewById(R.id.message);
        mRecycler = (RecyclerView)findViewById(R.id.recycler);
        //Layout manager untuk recyclerview
        mManager = new LinearLayoutManager(getApplicationContext());
        mRecycler.setLayoutManager(mManager);
        mList = new ArrayList<>();
        //Adapter untuk recyclerview
        mAdapter = new ChatAdapter(getApplicationContext(), mList, mUserId );
        mRecycler.setAdapter(mAdapter);
        //Listener untuk button send ketika diklik
        mSend.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                mFirstLoad = true;
                String data = mMessage.getText().toString();
                //Mengirimkan parse object berisi pesan dan userId ke parse.com

                if(mUserId == null){
                    Toast.makeText(getApplicationContext(),"Please wait, trying to login with" +
                            " anonymous account", Toast.LENGTH_SHORT).show();
                }else{
                    ParseObject message = ParseObject.create("Message");
                    message.put("userId", mUserId);
                    message.put("message", data);
                    message.saveInBackground(new SaveCallback() {
                        @Override
                        public void done(ParseException e) {
                            //Respon jika pesan terkirim ke parse.com
                            //Mengirimkan push ke semua device dengan channel "chat"
                            ParsePush parsePush = new ParsePush();
                            parsePush.setChannel("chat");
                            parsePush.setMessage("new message");
                            parsePush.sendInBackground();
                        }
                    });
                    mMessage.setText("");
                }

            }
        });
    }

    //Untuk mengatur isi dari recyclerview
    private void receiveMessages(){
        //Mengambil data dari parse.com
        ParseQuery<Message> query = ParseQuery.getQuery(Message.class);
        //Membatasi jumlah download row sebesar 50
        query.setLimit(50);
        //Order berdasarkan collumn createdAt pada table Message di parse.com secara Descending
        //Yang didapatkan adalah data terbaru
        query.orderByDescending("createdAt");
        query.findInBackground(new FindCallback<Message>() {
            public void done(List<Message> messages, ParseException e) {
                if (e == null) {
                    //Reset mList
                    if(mList.size()!=0)
                    mList.clear();
                    //Mengisikan data kembali dengan respon dari parse.com
                    mList.addAll(messages);
                    Collections.reverse(mList);
                    mAdapter.setData(mList);
                    mAdapter.notifyDataSetChanged();
                    if (mFirstLoad) {
                        mRecycler.scrollToPosition(mList.size() - 1);
                        mFirstLoad = false;
                    }
                } else {
                    Log.d("message", "Error: " + e.getMessage());
                }
            }
        });
    }


    //Respon dari Observer, jika ada push dari parse.com, maka akan memanggil receiveMessages()
    @Override
    public void update(Observable observable, Object o) {
        receiveMessages();
    }


}


Penjelasan dari class ini sudah saya tuliskan didalam class.

Package Adapter

ChatAdapter.java
package heends.simplechat.Adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;

import heends.simplechat.ParseObject.Message;
import heends.simplechat.R;
import heends.simplechat.ViewHolder.ChatHolder;

/**
 * Created by -H- on 10/15/2015.
 */
public class ChatAdapter extends RecyclerView.Adapter<ChatHolder> {

    private Context mContext;
    private List<Message> mList = Collections.emptyList();
    private String mMyId;
    private RelativeLayout.LayoutParams mParamsRight, mParamsLeft;

    public ChatAdapter(Context context, List<Message> list, String myId) {
        mContext = context;
        mList = list;
        mMyId = myId;
        mParamsRight = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);
        mParamsRight.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        mParamsLeft = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);
        mParamsLeft.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
    }

    public void setData(List<Message> list){
        mList = list;
    }

    @Override
    public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.custom_chat_layout, parent, false);
        ChatHolder chatHolder = new ChatHolder(view);
        return chatHolder;
    }

    @Override
    public void onBindViewHolder(final ChatHolder holder, int position) {
        Message message = mList.get(position);
        //Layout untuk tampilan kita sendiri, rata kanan
        if(message.getUserId().equals(mMyId)){
            holder.mGravatarLeft.setVisibility(ImageView.INVISIBLE);
            holder.mGravatarRight.setVisibility(ImageView.VISIBLE);
            holder.mMessage.setLayoutParams(mParamsRight);
            holder.mMessage.setBackgroundColor(mContext.getResources().getColor(R.color.colorPrimary));
            Picasso.with(mContext).load(setGravatar(message.getUserId())).into(holder.mGravatarRight);
        }else{
            //Layout untuk lawan chat, rata kiri
            holder.mGravatarLeft.setVisibility(ImageView.VISIBLE);
            holder.mGravatarRight.setVisibility(ImageView.INVISIBLE);
            holder.mMessage.setLayoutParams(mParamsLeft);
            holder.mMessage.setBackgroundColor(mContext.getResources().getColor(R.color.colorAccent));
            Picasso.with(mContext).load(setGravatar(message.getUserId())).into(holder.mGravatarLeft);
        }

        holder.mMessage.setText(message.getMessage());
        if(message.getMessage().equals(""))
            holder.mMessage.setVisibility(TextView.INVISIBLE);
        else
            holder.mMessage.setVisibility(TextView.VISIBLE);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }



    private String setGravatar(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";
    }
}

ChatAdapter adalah adapter untuk recyclerview. Dengan ChatAdapter ini kita membuat custom view untuk recycler view.

Package Application

MyApplication.java
package heends.simplechat.Application;

import android.app.Application;

import com.parse.Parse;
import com.parse.ParseInstallation;
import com.parse.ParseObject;
import com.parse.ParsePush;

import heends.simplechat.ParseObject.Message;

/**
 * Created by -H- on 10/15/2015.
 */
public class MyApplication extends Application {

    public static final String APPLICATION_ID = "0iOalFtKENXBllUbBt01N4QIXSsqt1GhqgiXb4On";
    public static final String CLIENT_KEY = "FfZjzulW9AdEbtbzcRwp16VSEjunlVNgt2F2E1rj";

    @Override
    public void onCreate() {
        super.onCreate();
        ParseObject.registerSubclass(Message.class);
        Parse.enableLocalDatastore(this);
        Parse.initialize(this, APPLICATION_ID, CLIENT_KEY);
        //subscribe berfungsi untuk memberikan channel untuk aplikasi ini
        ParsePush.subscribeInBackground("chat");
        ParseInstallation.getCurrentInstallation().saveInBackground();

    }
}

Class ini akan dipanggil pertama kali kita program berjalan. Class ini berfungsi untuk inisialisasi Parse dengan Application ID dan Client KEY yang didapatkan dari App di parse.com.

 

Package ParseObject

Message.java
package heends.simplechat.ParseObject;

import com.parse.ParseClassName;
import com.parse.ParseObject;

/**
 * Created by -H- on 10/15/2015.
 */
@ParseClassName("Message")
public class Message extends ParseObject {
    public String getUserId() {
        return getString("userId");
    }

    public String getMessage() {
        return getString("message");
    }


}

 

Package Receiver

ParseReceiver.java
package heends.simplechat.Receiver;

import android.content.Context;
import android.content.Intent;

import com.parse.ParsePushBroadcastReceiver;

import heends.simplechat.Utils.ObservableObject;

/**
 * Created by -H- on 10/15/2015.
 */
public class ParseReceiver extends ParsePushBroadcastReceiver {


    @Override
    protected void onPushReceive(Context context, Intent intent) {
        ObservableObject.getInstance().updateValue("");
    }


}

Ini adalah custom class untuk pushreceiver. Jadi jika menggunakan bawaan dari parse, maka akan muncul notifikasi jika terdapat push. Namun dengan class ini, ketika menerima push, maka akan memanggil method updateValue() yang telah diimplementasikan di class MainActivity. Diclass MainActivity, method ini digunakan untuk memanggil method receivceMesssages() yang berfungsi untuk mendapatkan message terbaru dari parse.com.

 

Package Utils

ObservableObject.class
package heends.simplechat.Utils;

import java.util.Observable;

/**
 * Created by -H- on 10/16/2015.
 */
public class ObservableObject extends Observable {
    private static ObservableObject instance = new ObservableObject();

    public static ObservableObject getInstance() {
        return instance;
    }

    private ObservableObject() {
    }

    public void updateValue(Object data) {
        synchronized (this) {
            setChanged();
            notifyObservers(data);
        }
    }
}

Class ini adalah singleton class, digunakan untuk menghubungkan Receiver dengan MainActivity.

 

Package ViewHolder

ChatHolder.java
package heends.simplechat.ViewHolder;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import heends.simplechat.R;

/**
 * Created by -H- on 10/15/2015.
 */
public class ChatHolder extends RecyclerView.ViewHolder {

    public ImageView mGravatarLeft, mGravatarRight;
    public TextView mMessage;

    public ChatHolder(View itemView) {
        super(itemView);
        mGravatarLeft = (ImageView)itemView.findViewById(R.id.gravatarLeft);
        mGravatarRight = (ImageView)itemView.findViewById(R.id.gravatarRight);
        mMessage = (TextView)itemView.findViewById(R.id.message);

    }
}

Class ini digunakan bersama dengan ChatAdapter.

 

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="heends.simplechat" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <!--
      IMPORTANT: Change "com.parse.starter.permission.C2D_MESSAGE" in the lines below
      to match your app's package name + ".permission.C2D_MESSAGE".
    -->
    <permission
        android:name="heends.simplechat.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="heends.simplechat.permission.C2D_MESSAGE" />
    <uses-permission android:name="android.permission.READ_PROFILE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />

    <application
        android:name=".Application.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".Activity.MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.parse.PushService" />

        <receiver
            android:name=".Receiver.ParseReceiver"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.parse.push.intent.RECEIVE" />
                <action android:name="com.parse.push.intent.DELETE" />
                <action android:name="com.parse.push.intent.OPEN" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="com.parse.GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

<!--                   IMPORTANT: Change "com.parse.starter" to match your app's package name. -->
                <category android:name="heends.simplechat" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

Banyak yang perlu diperhatikan di android manifest ini, pada <application, tambahkan android:name=”.Application.MyApplication”, fungsinya agar class MyApplication tadi berjalan waktu pertama kali android running. Jika ingin membuat projek baru, pastikan untuk mengganti heends.simplechat dengan nama package anda.

Jalankan aplikasi dan cobalah kirim pesan. Jika berhasil, maka akan menerima push dari parse.com dan recycler view akan terupdate datanya. Untuk mengecek data yang ada diparse.com, bisa dilihat dimenu Core.

11

 

Screenshot aplikasi:

device-2015-10-16-044511 device-2015-10-16-044726

 

Download Apk:

[maxbutton id=”1″ url=”http://www.andevindo.com/?smd_process_download=1&download_id=672″]

 

Download Sourcecode:

[maxbutton id=”1″ url=”http://www.andevindo.com/?smd_process_download=1&download_id=671″]

 

 

 

 

Share

You may also like...