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 article : Blog 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:
- Mengatur bagian parse.com (Account, new App, setting)
- Menyiapkan library yang diperlukan
- Mendisain tampilan
- 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.
- Klik Create a new App, beri nama dan klik button Create.
- Pilih App yang telah dibuat dan masuk ke setting.
- Masuk ke bagian menu Keys. Kita akan gunakan Application ID dan Client Key diaplikasi android.
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.
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:
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)
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)
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:
Berikut adalah alur jalannya aplikasi:
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.
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.
Screenshot aplikasi:
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″]