0

Android realtime chat dengan menggunakan Firebase Realtime Database

Pada artikel kali ini, saya akan membuat realtime chat yang sederhana dengan menggunakan Firebase Realtime Database. Firebase Realtime Database adalah database yang di-host di cloud. Data disimpan sebagai JSON dan disinkronkan secara realtime ke setiap klien yang terhubung. Ketika membuat aplikasi cross-platform dengan SDK Android, iOS, dan JavaScript, semua klien akan berbagi sebuah instance Realtime Database dan menerima update data terbaru secara otomatis.

Langkah pertama yang perlu dilakukan adalah membuat firebase project pada:

https://console.firebase.google.com

Setelah project berhasil dibuat, selanjutnya adalah membuat database pada firebase.

Ganti permission pada database, dengan cara buka menu Develop > Database. Pada dropdown pilih Realtime Database kemudian klik tab Rules. Edit rules menjadi:

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Langkah berikutnya adalah membuat Project baru pada Android Studio, dengan cara File -> New -> New Project.

Pada aplikasi android yang dibuat, akan dibagi menjadi tiga halaman utama, yaitu halaman untuk join. Yang kedua adalah halaman untuk membuat room. Dan yang terakhir adalah halaman untuk chatting.

Klik kanan pada project -> Activity -> Empty Activity. Kemudian beri nama JoinActivity.

Jangan lupa jadikan JoinActivity ini menjadi main activity dengan cara buka AndroidManifest.xml. Tambahkan intent-filter pada konfigurasi JoinActivity.

<activity
    android:name=".ui.login.LoginActivity"
    android:label="@string/title_activity_login">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Buka JoinActivity kemudian buat kode seperti berikut ini:

package org.akhal.example.qchat;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ChatActivity extends AppCompatActivity {

    private Button btnSend;
    private EditText etMessage;
    private TextView tvMessage;
    private DatabaseReference rootDb;
    private String username, room;
    private String tempKey;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);

        btnSend = findViewById(R.id.btn_send);
        etMessage = findViewById(R.id.et_message);
        tvMessage = findViewById(R.id.tv_message);

        QPrefs qPrefs = new QPrefs(this);
        username = qPrefs.getUsername();
        room = qPrefs.getRoom();
        setTitle("Room - "+room);

        rootDb = FirebaseDatabase.getInstance().getReference().child(room);

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Map<String, Object> map = new HashMap<String, Object>();
                tempKey = rootDb.push().getKey();
                rootDb.updateChildren(map);

                DatabaseReference msgRoot = rootDb.child(tempKey);
                Map<String, Object> map2 = new HashMap<String, Object>();
                map2.put("name",username);
                map2.put("msg",etMessage.getText().toString());
                msgRoot.updateChildren(map2);
            }
        });

        rootDb.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                append_chat_conversatin(dataSnapshot);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                append_chat_conversatin(dataSnapshot);
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

    }

    private void append_chat_conversatin(DataSnapshot dataSnapshot) {
        Iterator i = dataSnapshot.getChildren().iterator();
        while (i.hasNext()){
            String msg = (String) ((DataSnapshot)i.next()).getValue();
            String username = (String) ((DataSnapshot)i.next()).getValue();
            tvMessage.append(username + " : "+msg +"\n");


        }
    }
}

Untuk activiy_join.xml buat menjadi seperti berikut:

<?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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingBottom="@dimen/activity_vertical_margin">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical">

        <EditText
            android:id="@+id/et_join"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:hint="username"/>

        <Button
            android:id="@+id/btn_join"
            android:layout_marginTop="20dp"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="Join"/>

    </LinearLayout>
</LinearLayout>

Kemudian buka MainActivity dan tambahkan kode sehingga menjadi seperti di bawah ini:

package org.akhal.example.qchat;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MainActivity extends AppCompatActivity {

    private Button btnRoom;
    private EditText etRoom;
    private ListView lvRoom;
    private Context context;
    private DatabaseReference dbRoot = FirebaseDatabase.getInstance().getReference().getRoot();
    private ArrayAdapter<String> arrayAdapter;
    private ArrayList<String> list_of_rooms = new ArrayList();

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

        context = this;
        btnRoom = findViewById(R.id.btn_room);
        etRoom = findViewById(R.id.et_room);
        lvRoom = findViewById(R.id.lv_room);

        arrayAdapter = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_1,list_of_rooms);
        lvRoom.setAdapter(arrayAdapter);

        btnRoom.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put(etRoom.getText().toString(),"");
                dbRoot.updateChildren(map);
            }
        });

        dbRoot.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                Set<String> set = new HashSet<String>();
                Iterator i = dataSnapshot.getChildren().iterator();
                while ( i.hasNext()) {
                    set.add(((DataSnapshot)i.next()).getKey());
                }
                list_of_rooms.clear();
                list_of_rooms.addAll(set);
                arrayAdapter.notifyDataSetChanged();
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

        lvRoom.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                QPrefs qPrefs = new QPrefs(context);
                qPrefs.setRoom(((TextView)view).getText().toString());
                Intent I = new Intent(getApplicationContext(), ChatActivity.class);
                startActivity(I);
            }
        });

    }
}

Untuk activity_main.xml menjadi seperti berikut:

<?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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <ListView
        android:id="@+id/lv_room"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_above="@+id/et_room">
    </ListView>

    <EditText
        android:id="@+id/et_room"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:layout_toStartOf="@+id/btn_room"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@+id/btn_room" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Room"
        android:id="@+id/btn_room"
        android:layout_gravity="right"
        style="?android:attr/borderlessButtonStyle"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:drawableLeft="@drawable/ic_add_black_24dp" />
</RelativeLayout>

Halaman terakhir adalah ChatActivity. Buat ChatActivity kemudian ganti code menjadi seperti berikut ini:

package org.akhal.example.qchat;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ChatActivity extends AppCompatActivity {

    private Button btnSend;
    private EditText etMessage;
    private TextView tvMessage;
    private DatabaseReference rootDb;
    private String username, room;
    private String tempKey;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);

        btnSend = findViewById(R.id.btn_send);
        etMessage = findViewById(R.id.et_message);
        tvMessage = findViewById(R.id.tv_message);

        QPrefs qPrefs = new QPrefs(this);
        username = qPrefs.getUsername();
        room = qPrefs.getRoom();
        setTitle("Room - "+room);

        rootDb = FirebaseDatabase.getInstance().getReference().child(room);

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Map<String, Object> map = new HashMap<String, Object>();
                tempKey = rootDb.push().getKey();
                rootDb.updateChildren(map);

                DatabaseReference msgRoot = rootDb.child(tempKey);
                Map<String, Object> map2 = new HashMap<String, Object>();
                map2.put("name",username);
                map2.put("msg",etMessage.getText().toString());
                msgRoot.updateChildren(map2);
            }
        });

        rootDb.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                append_chat_conversatin(dataSnapshot);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                append_chat_conversatin(dataSnapshot);
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

    }

    private void append_chat_conversatin(DataSnapshot dataSnapshot) {
        Iterator i = dataSnapshot.getChildren().iterator();
        while (i.hasNext()){
            String msg = (String) ((DataSnapshot)i.next()).getValue();
            String username = (String) ((DataSnapshot)i.next()).getValue();
            tvMessage.append(username + " : "+msg +"\n");


        }
    }
}

Dan activity_chat.xml menjadi seperti berikut:

<?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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:weightSum="1">

    <ScrollView
        android:id="@+id/sv_message"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true">

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="1">
        <EditText
            android:id="@+id/et_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/button"
            android:layout_alignParentStart="true"
            android:layout_toStartOf="@+id/button"
            android:layout_weight="1"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@+id/button" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="56dp"
            android:layout_height="wrap_content"
            style="?android:attr/borderlessButtonStyle"
            android:drawableLeft="@drawable/ic_send_black_24dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true" />

    </LinearLayout>

</LinearLayout>

Langkah terakhir adalah membuat shared preferences. Shared preferences digunakan untuk menyimpan username yang digunakan dan room yang dipilih. Buat file dengan nama QPrefs.

package org.akhal.example.qchat;

import android.content.Context;
import android.content.SharedPreferences;

/**
 * Created by Akhal on 2020/06/3.
 */
public class QPrefs {

    private SharedPreferences pref;

    public QPrefs(Context context) {
        pref = context.getSharedPreferences("qpref", context.MODE_PRIVATE);
    }

    public void setUsername(String username) {
        pref.edit().putString("username", username).commit();
    }

    public String getUsername() {
        return  pref.getString("username", "");
    }

    public void setRoom(String room) {
        pref.edit().putString("room", room).commit();
    }

    public String getRoom() {
        return  pref.getString("room", "");
    }
}

Jika sudah selesai, jalankan aplikasi yang dibuat.