در قسمت اول و قسمت دوم در مقاله ساخت اپلیکیشن چت با استفاده از فایربیس، ما برنامهی سرور شامل REST API و پنل مدیریت را ساختیم و همچنین نمونه سازی از gcm یکپارچه را با چند تست ابتدایی آموزش دادیم.
این قسمت سوم ساخت اپلیکیشن چت با فایربیس یک سناریوی کامل و پیشفرض از کاربرد GCM را با فراهم کردن اطلاعات مهمی – مانند انجام اعمال پویا و بروزرسانی UI بسته به نوع اعلان – نمایش میدهد. فعالیتهایی مثل افزایش شمارندهی پیام در لیست، نمایش خودکار پیام در صفحه نمایش چت توضیح خواهد شد. با قسمت سوم ساخت اپلیکیشن فایربیس و PHP با تیک4 همراه باشید.
ساخت برنامهی چت لحظهای
برنامهی چت در اصل شامل سه صفحه است. اول صفحهی لاگین است، جایی که کاربر نام و ایمیل خود را وارد میکند. دوم صفحهی لیست اتاق چت است، جایی که لیست اتاق چتها قرار دارد. صفحهی سوم بحث یک اتاق چت، جایی که پیامهای بحث به سمت چپ و راست، تراز میشوند.
افزودن پشتیبانی از volley
ما از کتابخانهی volley اندروید برای ساخت تمام درخواستهای endpoint های apiی rest استفاده میکنیم. Volley یک راه آسان برای مدیریت درخواستهای http و پاسخ فراهم میکند.
- فایل build.gradle که در زیر پوشهی app قرار دارد باز کنید و وابستگی volley را اضافه کنید و پروژه را دوباره build کنید.
build.gradle
dependencies {
...
compile 'com.mcxiaoke.volley:library-aar:1.0.0'
}
- فایل MyApplication.java که در زیر پکیج app قرار دارد باز کنید و کد را مطابق کد زیر تغییر دهید. اینجا ما یک نمونهی singleton از RequestQueueی volley ایجاد میکنیم.
MyApplication.java
package info.tik4.gcm.app;
/**
* Created by Lincoln on 14/10/15.
*/
import android.app.Application;
import android.content.Intent;
import android.text.TextUtils;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import info.tik4.gcm.activity.LoginActivity;
import info.tik4.gcm.helper.MyPreferenceManager;
/**
* Created by Ravi on 13/05/15.
*/
public class MyApplication extends Application {
public static final String TAG = MyApplication.class
.getSimpleName();
private RequestQueue mRequestQueue;
private static MyApplication mInstance;
private MyPreferenceManager pref;
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized MyApplication getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public MyPreferenceManager getPrefManager() {
if (pref == null) {
pref = new MyPreferenceManager(this);
}
return pref;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
public void logout() {
pref.clear();
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
3.یک پکیج به نام model ایجاد کنید. درون پکیج model سه کلاس به نامهای user.java، Message.java و ChatRoom.java را ایجاد کنید. این کلاسها برای ایجاد اشیاء در زمان پردازش پاسخ جیسون استفاده خواهند شد. ممکن است متوجه شده باشید که این کلاسها Serializable پیاده سازی کردهایم، که به ما اجازه میدهد اشیاء را با استفاده از intent ها به یک activity پاس بدهیم.
User.java
package info.tik4.gcm.model;
import java.io.Serializable;
public class User implements Serializable {
String id, name, email;
public User() {
}
public User(String id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Message.java
package info.tik4.gcm.model;
import java.io.Serializable;
public class Message implements Serializable {
String id, message, createdAt;
User user;
public Message() {
}
public Message(String id, String message, String createdAt, User user) {
this.id = id;
this.message = message;
this.createdAt = createdAt;
this.user = user;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
ChatRoom.java
package info.tik4.gcm.model;
import java.io.Serializable;
public class ChatRoom implements Serializable {
String id, name, lastMessage, timestamp;
int unreadCount;
public ChatRoom() {
}
public ChatRoom(String id, String name, String lastMessage, String timestamp, int unreadCount) {
this.id = id;
this.name = name;
this.lastMessage = lastMessage;
this.timestamp = timestamp;
this.unreadCount = unreadCount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastMessage() {
return lastMessage;
}
public void setLastMessage(String lastMessage) {
this.lastMessage = lastMessage;
}
public int getUnreadCount() {
return unreadCount;
}
public void setUnreadCount(int unreadCount) {
this.unreadCount = unreadCount;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
- فایل MyPreferenceManager.java را باز کنید و متدهای storeUser() و getUser() را که اطلاعات کاربر را در preferences مشترک ذخیره میکند، اضافه کنید. این متدها وقتی که کاربر با موفقیت لاگین شد فراخوانی میشوند.
MyPreferenceManager.java
package info.tik4.gcm.helper;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import info.tik4.gcm.model.User;
/**
* Created by Lincoln on 07/01/16.
*/
public class MyPreferenceManager {
private String TAG = MyPreferenceManager.class.getSimpleName();
// Shared Preferences
SharedPreferences pref;
// Editor for Shared preferences
SharedPreferences.Editor editor;
// Context
Context _context;
// Shared pref mode
int PRIVATE_MODE = 0;
// Sharedpref file name
private static final String PREF_NAME = "tik4_gcm";
// All Shared Preferences Keys
private static final String KEY_USER_ID = "user_id";
private static final String KEY_USER_NAME = "user_name";
private static final String KEY_USER_EMAIL = "user_email";
private static final String KEY_NOTIFICATIONS = "notifications";
// Constructor
public MyPreferenceManager(Context context) {
this._context = context;
pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
editor = pref.edit();
}
public void storeUser(User user) {
editor.putString(KEY_USER_ID, user.getId());
editor.putString(KEY_USER_NAME, user.getName());
editor.putString(KEY_USER_EMAIL, user.getEmail());
editor.commit();
Log.e(TAG, "User is stored in shared preferences. " + user.getName() + ", " + user.getEmail());
}
public User getUser() {
if (pref.getString(KEY_USER_ID, null) != null) {
String id, name, email;
id = pref.getString(KEY_USER_ID, null);
name = pref.getString(KEY_USER_NAME, null);
email = pref.getString(KEY_USER_EMAIL, null);
User user = new User(id, name, email);
return user;
}
return null;
}
public void addNotification(String notification) {
// get old notifications
String oldNotifications = getNotifications();
if (oldNotifications != null) {
oldNotifications += "|" + notification;
} else {
oldNotifications = notification;
}
editor.putString(KEY_NOTIFICATIONS, oldNotifications);
editor.commit();
}
public String getNotifications() {
return pref.getString(KEY_NOTIFICATIONS, null);
}
public void clear() {
editor.clear();
editor.commit();
}
}
- EndPoints.java را درون پکیج app ایجاد کنید. اینجا ما یوآرالهای endpoint REST API اعلان میکنیم. اگر شما برنامه را به صورت محلی تست میکنید، از آدرس ip کامپیوتری که سرویس php برروی آن در حال اجرا است استفاده کنید. با ادامه مقاله ساخت اپلیکیشن فایربیس و PHP همراه ما باشید.
EndPoints.java
package info.tik4.gcm.app;
public class EndPoints {
// localhost url -
public static final String BASE_URL = "http://192.168.0.101/gcm_chat/v1";
public static final String LOGIN = BASE_URL + "/user/login";
public static final String USER = BASE_URL + "/user/_ID_";
public static final String CHAT_ROOMS = BASE_URL + "/chat_rooms";
public static final String CHAT_THREAD = BASE_URL + "/chat_rooms/_ID_";
public static final String CHAT_ROOM_MESSAGE = BASE_URL + "/chat_rooms/_ID_/message";
}
- GcmIntentService.java که در زیر بستهی gcm قرار دارد باز کنید و کد را به صورت زیر تغییر دهید. اینجا ما کد متد sendRegistrationToServer()تغییر داده ایم برای اینکه توکن ثبت نام gcm را به سرورمان ارسال کند تا پایگاه دادهی MySQL بروزرسانی شود.
package info.tik4.gcm.gcm;
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.google.android.gms.gcm.GcmPubSub;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import info.tik4.gcm.R;
import info.tik4.gcm.app.Config;
import info.tik4.gcm.app.EndPoints;
import info.tik4.gcm.app.MyApplication;
import info.tik4.gcm.model.User;
public class GcmIntentService extends IntentService {
private static final String TAG = GcmIntentService.class.getSimpleName();
public GcmIntentService() {
super(TAG);
}
public static final String KEY = "key";
public static final String TOPIC = "topic";
public static final String SUBSCRIBE = "subscribe";
public static final String UNSUBSCRIBE = "unsubscribe";
@Override
protected void onHandleIntent(Intent intent) {
String key = intent.getStringExtra(KEY);
switch (key) {
case SUBSCRIBE:
// subscribe to a topic
String topic = intent.getStringExtra(TOPIC);
subscribeToTopic(topic);
break;
case UNSUBSCRIBE:
break;
default:
// if key is specified, register with GCM
registerGCM();
}
}
/**
* Registering with GCM and obtaining the gcm registration id
*/
private void registerGCM() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Log.e(TAG, "GCM Registration Token: " + token);
// sending the registration id to our server
sendRegistrationToServer(token);
sharedPreferences.edit().putBoolean(Config.SENT_TOKEN_TO_SERVER, true).apply();
} catch (Exception e) {
Log.e(TAG, "Failed to complete token refresh", e);
sharedPreferences.edit().putBoolean(Config.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
private void sendRegistrationToServer(final String token) {
// checking for valid login session
User user = MyApplication.getInstance().getPrefManager().getUser();
if (user == null) {
// TODO
// user not found, redirecting him to login screen
return;
}
String endPoint = EndPoints.USER.replace("_ID_", user.getId());
Log.e(TAG, "endpoint: " + endPoint);
StringRequest strReq = new StringRequest(Request.Method.PUT,
endPoint, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e(TAG, "response: " + response);
try {
JSONObject obj = new JSONObject(response);
// check for error
if (obj.getBoolean("error") == false) {
// broadcasting token sent to server
Intent registrationComplete = new Intent(Config.SENT_TOKEN_TO_SERVER);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(registrationComplete);
} else {
Toast.makeText(getApplicationContext(), "Unable to send gcm registration id to our sever. " + obj.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Json parse error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, "Volley error: " + error.getMessage() + ", code: " + networkResponse);
Toast.makeText(getApplicationContext(), "Volley error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("gcm_registration_id", token);
Log.e(TAG, "params: " + params.toString());
return params;
}
};
//Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
/**
* Subscribe to a topic
*/
public static void subscribeToTopic(String topic) {
GcmPubSub pubSub = GcmPubSub.getInstance(MyApplication.getInstance().getApplicationContext());
InstanceID instanceID = InstanceID.getInstance(MyApplication.getInstance().getApplicationContext());
String token = null;
try {
token = instanceID.getToken(MyApplication.getInstance().getApplicationContext().getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
if (token != null) {
pubSub.subscribe(token, "/topics/" + topic, null);
Log.e(TAG, "Subscribed to topic: " + topic);
} else {
Log.e(TAG, "error: gcm registration id is null");
}
} catch (IOException e) {
Log.e(TAG, "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage());
Toast.makeText(MyApplication.getInstance().getApplicationContext(), "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void unsubscribeFromTopic(String topic) {
GcmPubSub pubSub = GcmPubSub.getInstance(getApplicationContext());
InstanceID instanceID = InstanceID.getInstance(getApplicationContext());
String token = null;
try {
token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
if (token != null) {
pubSub.unsubscribe(token, "");
Log.e(TAG, "Unsubscribed from topic: " + topic);
} else {
Log.e(TAG, "error: gcm registration id is null");
}
} catch (IOException e) {
Log.e(TAG, "Topic unsubscribe error. Topic: " + topic + ", error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Topic subscribe error. Topic: " + topic + ", error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
اضافه کردن صفحه لاگین
با تمام فایلهای مورد نیاز در این محل، ما صفحهی اول برنامه را که همان صفحهی لاگین است اضافه کنیم. صفحهی لاگین برای این که نام و ایمیل کاربر را بگیرد اضافه میشود. توجه داشته باشید که این روش درستی برای احراز هویت نیست چون ورود پسورد ضروری نیست. هر کاربری با وارد کردن هر نام تصادفی و ایمیلی میتواند به برنامهی ما لاگین بشود. ساخت اپلیکیشن فایربیس و PHP همراه ما باشید.
- فایل strings.xml که در زیر values قرار دارد باز کنید و مقدار string زیر را به آن اضافه کنید.
strings.xml
<resources>
<string name="app_name">Google Cloud Messaging</string>
<string name="action_settings">Settings</string>
<string name="title_activity_login">LoginActivity</string>
<string name="hint_name">Full Name</string>
<string name="hint_email">Email</string>
<string name="err_msg_name">Enter full name</string>
<string name="err_msg_email">Enter valid email address</string>
<string name="title_activity_chat_room_discussion">ChatRoomDiscussionActivity</string>
<string name="action_logout">Logout</string>
</resources>
- فایل colors.xml را باز کنید و مقدار رنگ زیر را به آن اضافه کنید.
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#7B1FA2</color>
<color name="colorPrimaryDark">#6A1B9A</color>
<color name="colorAccent">#EA80FC</color>
<color name="list_divider">#dedede</color>
<color name="bg_bubble_self">#E1E1E1</color>
</resources>
- یک activity به نام LoginActivity.java با کلیک راست بر activity ⇒ New ⇒ Activity ⇒ Blank Activity ایجاد کنید. این همچنین فایلهای layout activity_login.xml و activity_login.xml layout را برای login activity میسازد.
- فایل activity_login.xml را باز کنید و layout را به صورت زیر تغییر دهید. اینجا ما appbar و یک toolbar اضافه کردهایم.
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="info.tik4.gcm.activity.LoginActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_login" />
</android.support.design.widget.CoordinatorLayout>
- فایل activity_login.xml را باز کنید و یک فرم لاگین ساده با فیلد ورودی نام و ایمیل در آن قرار دهید.
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="info.tik4.gcm.activity.LoginActivity"
tools:showIn="@layout/activity_login">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:id="@+id/input_layout_name"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/input_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_name" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/input_layout_email"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/input_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_email" />
</android.support.design.widget.TextInputLayout>
<Button android:id="@+id/btn_enter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Enter" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:indeterminateTint="@color/colorAccent"
android:visibility="gone"
android:indeterminateTintMode="src_atop" />
</RelativeLayout>
- LoginActivity.java را باز کنید و کد زیر را در آن وارد کنید.
- قبل از تنظیم contentView ما نشست کاربر را در preferences مشترک بررسی خواهیم کرد. اگر کاربر در حال حاضر لاگین شده باشد. MainActivity لانچ خواهد شد.
- متد login() یک درخواست http با پاس کردن نام و ایمیل به عنوان پارامترهای پست به endpoint لاگین میسازد. یک کاربر جدید در سرور ایجاد خواهد شد و پاسخ جیسون ارائه خواهد شد.
- بعد از پردازش json، نشست(session) کاربر با ذخیرهی شی(object) کاربر در shared preferences ایجاد خواهد شد و MainActivity اجرا خواهد شد.
LoginActivity.java
package info.tik4.gcm.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import info.tik4.gcm.R;
import info.tik4.gcm.app.EndPoints;
import info.tik4.gcm.app.MyApplication;
import info.tik4.gcm.model.User;
public class LoginActivity extends AppCompatActivity {
private String TAG = LoginActivity.class.getSimpleName();
private EditText inputName, inputEmail;
private TextInputLayout inputLayoutName, inputLayoutEmail;
private Button btnEnter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Check for login session. It user is already logged in
* redirect him to main activity
* */
if (MyApplication.getInstance().getPrefManager().getUser() != null) {
startActivity(new Intent(this, MainActivity.class));
finish();
}
setContentView(R.layout.activity_login);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
inputLayoutName = (TextInputLayout) findViewById(R.id.input_layout_name);
inputLayoutEmail = (TextInputLayout) findViewById(R.id.input_layout_email);
inputName = (EditText) findViewById(R.id.input_name);
inputEmail = (EditText) findViewById(R.id.input_email);
btnEnter = (Button) findViewById(R.id.btn_enter);
inputName.addTextChangedListener(new MyTextWatcher(inputName));
inputEmail.addTextChangedListener(new MyTextWatcher(inputEmail));
btnEnter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
login();
}
});
}
/**
* logging in user. Will make http post request with name, email
* as parameters
*/
private void login() {
if (!validateName()) {
return;
}
if (!validateEmail()) {
return;
}
final String name = inputName.getText().toString();
final String email = inputEmail.getText().toString();
StringRequest strReq = new StringRequest(Request.Method.POST,
EndPoints.LOGIN, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e(TAG, "response: " + response);
try {
JSONObject obj = new JSONObject(response);
// check for error flag
if (obj.getBoolean("error") == false) {
// user successfully logged in
JSONObject userObj = obj.getJSONObject("user");
User user = new User(userObj.getString("user_id"),
userObj.getString("name"),
userObj.getString("email"));
// storing user in shared preferences
MyApplication.getInstance().getPrefManager().storeUser(user);
// start main activity
startActivity(new Intent(getApplicationContext(), MainActivity.class));
finish();
} else {
// login error - simply toast the message
Toast.makeText(getApplicationContext(), "" + obj.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Json parse error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, "Volley error: " + error.getMessage() + ", code: " + networkResponse);
Toast.makeText(getApplicationContext(), "Volley error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("name", name);
params.put("email", email);
Log.e(TAG, "params: " + params.toString());
return params;
}
};
//Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
private void requestFocus(View view) {
if (view.requestFocus()) {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
}
// Validating name
private boolean validateName() {
if (inputName.getText().toString().trim().isEmpty()) {
inputLayoutName.setError(getString(R.string.err_msg_name));
requestFocus(inputName);
return false;
} else {
inputLayoutName.setErrorEnabled(false);
}
return true;
}
// Validating email
private boolean validateEmail() {
String email = inputEmail.getText().toString().trim();
if (email.isEmpty() || !isValidEmail(email)) {
inputLayoutEmail.setError(getString(R.string.err_msg_email));
requestFocus(inputEmail);
return false;
} else {
inputLayoutEmail.setErrorEnabled(false);
}
return true;
}
private static boolean isValidEmail(String email) {
return !TextUtils.isEmpty(email) && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches();
}
private class MyTextWatcher implements TextWatcher {
private View view;
private MyTextWatcher(View view) {
this.view = view;
}
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
public void afterTextChanged(Editable editable) {
switch (view.getId()) {
case R.id.input_name:
validateName();
break;
case R.id.input_email:
validateEmail();
break;
}
}
}
}
- AndroidManifest.xml را باز کنید و LoginActivity را به عنوان Launcher activity قرار دهید.

افزودن صفحهی لیست چتروم
اتاقهای چت در یک مدل لیست RecyclerView که ما استفاده کردیم نمایش داده خواهند شد. بنابراین پشتیبانی recycler view را به build.gradle اضافه کنید.
- فایل build.gradle را باز کنید و وابستگی RecyclerView را به آن اضافه کنید و پروژه را rebuild کنید.
compile ‘com.android.support:recyclerview-v7:23.1.1’
در زیر وابستگیهای نهایی build.gradle ما قرار دارند
build.gradle
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.mcxiaoke.volley:library-aar:1.0.0'
compile 'com.android.support:recyclerview-v7:23.1.1'
compile "com.google.android.gms:play-services:8.3.0"
}
- layoutی به نام chat_rooms_list_row.xml ایجاد کنید. این layout اطلاعات یک اتاق چت تنها مثل نام، آخرین پیام، تعداد پیامهای خوانده نشده و timestamp را در recycler view رندر میکند
chat_rooms_list_row
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textColor="#444444"
android:textStyle="bold"
android:textSize="16dp" />
<TextView android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:textColor="#888888"
android:layout_marginTop="5dp"
android:text="Seems gcm will take some time"/>
<TextView android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12:00 AM"
android:textSize="10dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"/>
<TextView android:id="@+id/count"
android:layout_width="20dp"
android:layout_height="20dp"
android:gravity="center"
android:textSize="10dp"
android:textColor="@android:color/white"
android:layout_below="@id/timestamp"
android:layout_marginTop="5dp"
android:layout_alignParentRight="true"
android:text="5"
android:background="@drawable/bg_circle"/>
</RelativeLayout>
- یک پکیج جدید به نام adapter ایجاد کنید. زیر adapter یک کلاس به نام ChatRoomsAdapter.java ایجاد کنید. این یک recycler view adapter است که داده را برای list view اتاق چت آماده میکند
ChatRoomsAdapter.java
package info.tik4.gcm.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import info.tik4.gcm.R;
import info.tik4.gcm.model.ChatRoom;
public class ChatRoomsAdapter extends RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder> {
private Context mContext;
private ArrayList<ChatRoom> chatRoomArrayList;
private static String today;
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView name, message, timestamp, count;
public ViewHolder(View view) {
super(view);
name = (TextView) view.findViewById(R.id.name);
message = (TextView) view.findViewById(R.id.message);
timestamp = (TextView) view.findViewById(R.id.timestamp);
count = (TextView) view.findViewById(R.id.count);
}
}
public ChatRoomsAdapter(Context mContext, ArrayList<ChatRoom> chatRoomArrayList) {
this.mContext = mContext;
this.chatRoomArrayList = chatRoomArrayList;
Calendar calendar = Calendar.getInstance();
today = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_rooms_list_row, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ChatRoom chatRoom = chatRoomArrayList.get(position);
holder.name.setText(chatRoom.getName());
holder.message.setText(chatRoom.getLastMessage());
if (chatRoom.getUnreadCount() > 0) {
holder.count.setText(String.valueOf(chatRoom.getUnreadCount()));
holder.count.setVisibility(View.VISIBLE);
} else {
holder.count.setVisibility(View.GONE);
}
holder.timestamp.setText(getTimeStamp(chatRoom.getTimestamp()));
}
@Override
public int getItemCount() {
return chatRoomArrayList.size();
}
public static String getTimeStamp(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestamp = "";
today = today.length() < 2 ? "0" + today : today;
try {
Date date = format.parse(dateStr);
SimpleDateFormat todayFormat = new SimpleDateFormat("dd");
String dateToday = todayFormat.format(date);
format = dateToday.equals(today) ? new SimpleDateFormat("hh:mm a") : new SimpleDateFormat("dd LLL, hh:mm a");
String date1 = format.format(date);
timestamp = date1.toString();
} catch (ParseException e) {
e.printStackTrace();
}
return timestamp;
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
public static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ChatRoomsAdapter.ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ChatRoomsAdapter.ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
}
- فایل MyGcmPushReceiver.java را باز کنید و کد را به صورت زیر تغییر دهید. اینجا ما چند متد برای مدیریت پوش نوتیفیکیشنها اضافه کردهایم. اول پوش نوتیفیکیشن با flag نود مشخص میشود و بعد فعالیت درست انجام خواهد شد.
- متد processChatRoomPush() هر گاه که پیام پوش اتاق چت دریافت شود فراخوانی خواهد شد
- متد processUserMessage() هرگاه که پوش مختص کاربر لاگین شده است فراخوانی خواهد شد
MyGcmPushReceiver.java
package info.tik4.gcm.gcm;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.gcm.GcmListenerService;
import org.json.JSONException;
import org.json.JSONObject;
import info.tik4.gcm.activity.ChatRoomActivity;
import info.tik4.gcm.activity.MainActivity;
import info.tik4.gcm.app.Config;
import info.tik4.gcm.app.MyApplication;
import info.tik4.gcm.model.Message;
import info.tik4.gcm.model.User;
public class MyGcmPushReceiver extends GcmListenerService {
private static final String TAG = MyGcmPushReceiver.class.getSimpleName();
private NotificationUtils notificationUtils;
/**
* Called when message is received.
*
* @param from SenderID of the sender.
* @param bundle Data bundle containing message data as key/value pairs.
* For Set of keys use data.keySet().
*/
@Override
public void onMessageReceived(String from, Bundle bundle) {
String title = bundle.getString("title");
Boolean isBackground = Boolean.valueOf(bundle.getString("is_background"));
String flag = bundle.getString("flag");
String data = bundle.getString("data");
Log.d(TAG, "From: " + from);
Log.d(TAG, "title: " + title);
Log.d(TAG, "isBackground: " + isBackground);
Log.d(TAG, "flag: " + flag);
Log.d(TAG, "data: " + data);
if (flag == null)
return;
if(MyApplication.getInstance().getPrefManager().getUser() == null){
// user is not logged in, skipping push notification
Log.e(TAG, "user is not logged in, skipping push notification");
return;
}
if (from.startsWith("/topics/")) {
// message received from some topic.
} else {
// normal downstream message.
}
switch (Integer.parseInt(flag)) {
case Config.PUSH_TYPE_CHATROOM:
// push notification belongs to a chat room
processChatRoomPush(title, isBackground, data);
break;
case Config.PUSH_TYPE_USER:
// push notification is specific to user
processUserMessage(title, isBackground, data);
break;
}
}
/**
* Processing chat room push message
* this message will be broadcasts to all the activities registered
* */
private void processChatRoomPush(String title, boolean isBackground, String data) {
if (!isBackground) {
try {
JSONObject datObj = new JSONObject(data);
String chatRoomId = datObj.getString("chat_room_id");
JSONObject mObj = datObj.getJSONObject("message");
Message message = new Message();
message.setMessage(mObj.getString("message"));
message.setId(mObj.getString("message_id"));
message.setCreatedAt(mObj.getString("created_at"));
JSONObject uObj = datObj.getJSONObject("user");
// skip the message if the message belongs to same user as
// the user would be having the same message when he was sending
// but it might differs in your scenario
if (uObj.getString("user_id").equals(MyApplication.getInstance().getPrefManager().getUser().getId())) {
Log.e(TAG, "Skipping the push message as it belongs to same user");
return;
}
User user = new User();
user.setId(uObj.getString("user_id"));
user.setEmail(uObj.getString("email"));
user.setName(uObj.getString("name"));
message.setUser(user);
// verifying whether the app is in background or foreground
if (!NotificationUtils.isAppIsInBackground(getApplicationContext())) {
// app is in foreground, broadcast the push message
Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION);
pushNotification.putExtra("type", Config.PUSH_TYPE_CHATROOM);
pushNotification.putExtra("message", message);
pushNotification.putExtra("chat_room_id", chatRoomId);
LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
// play notification sound
NotificationUtils notificationUtils = new NotificationUtils();
notificationUtils.playNotificationSound();
} else {
// app is in background. show the message in notification try
Intent resultIntent = new Intent(getApplicationContext(), ChatRoomActivity.class);
resultIntent.putExtra("chat_room_id", chatRoomId);
showNotificationMessage(getApplicationContext(), title, user.getName() + " : " + message.getMessage(), message.getCreatedAt(), resultIntent);
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Json parse error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
} else {
// the push notification is silent, may be other operations needed
// like inserting it in to SQLite
}
}
/**
* Processing user specific push message
* It will be displayed with / without image in push notification tray
* */
private void processUserMessage(String title, boolean isBackground, String data) {
if (!isBackground) {
try {
JSONObject datObj = new JSONObject(data);
String imageUrl = datObj.getString("image");
JSONObject mObj = datObj.getJSONObject("message");
Message message = new Message();
message.setMessage(mObj.getString("message"));
message.setId(mObj.getString("message_id"));
message.setCreatedAt(mObj.getString("created_at"));
JSONObject uObj = datObj.getJSONObject("user");
User user = new User();
user.setId(uObj.getString("user_id"));
user.setEmail(uObj.getString("email"));
user.setName(uObj.getString("name"));
message.setUser(user);
// verifying whether the app is in background or foreground
if (!NotificationUtils.isAppIsInBackground(getApplicationContext())) {
// app is in foreground, broadcast the push message
Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION);
pushNotification.putExtra("type", Config.PUSH_TYPE_USER);
pushNotification.putExtra("message", message);
LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
// play notification sound
NotificationUtils notificationUtils = new NotificationUtils();
notificationUtils.playNotificationSound();
} else {
// app is in background. show the message in notification try
Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class);
// check for push notification image attachment
if (TextUtils.isEmpty(imageUrl)) {
showNotificationMessage(getApplicationContext(), title, user.getName() + " : " + message.getMessage(), message.getCreatedAt(), resultIntent);
} else {
// push notification contains image
// show it with the image
showNotificationMessageWithBigImage(getApplicationContext(), title, message.getMessage(), message.getCreatedAt(), resultIntent, imageUrl);
}
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Json parse error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
} else {
// the push notification is silent, may be other operations needed
// like inserting it in to SQLite
}
}
/**
* Showing notification with text only
* */
private void showNotificationMessage(Context context, String title, String message, String timeStamp, Intent intent) {
notificationUtils = new NotificationUtils(context);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
notificationUtils.showNotificationMessage(title, message, timeStamp, intent);
}
/**
* Showing notification with text and image
* */
private void showNotificationMessageWithBigImage(Context context, String title, String message, String timeStamp, Intent intent, String imageUrl) {
notificationUtils = new NotificationUtils(context);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
notificationUtils.showNotificationMessage(title, message, timeStamp, intent, imageUrl);
}
}
18.یک لایوته xml به نام line_divider.xml درون پوشهی drawable بسازید
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="@color/list_divider" />
</shape>
- یک فایل xml به نام bg_circle.xml درون پوشهی drawable بسازید. این لایوت به عنوان پس زمینهی گرد برای شماره پیامهای خوانده نشده عمل میکند.
bg_circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/colorPrimary" />
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
- زیر پکیج helper، SimpleDividerItemDecoration.java را ایجاد کنید. این کلاس به اضافه شدن جداکننده به آیتمهای recycler view کمک میکند.
SimpleDividerItemDecoration.java
package info.tik4.gcm.helper;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import info.tik4.gcm.R;
/**
* Created by Lincoln on 30/10/15.
*/
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
public SimpleDividerItemDecoration(Context context) {
mDivider = ContextCompat.getDrawable(context, R.drawable.line_divider);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
- فایل menu_main.xml که در زیر res menu قرار دارد را باز کنید و یک آیتم منوی logout برای فراهم کردن امکان logout به کاربر اضافه کنید.
menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".activity.MainActivity">
<item
android:id="@+id/action_logout"
android:orderInCategory="100"
android:title="@string/action_logout"
app:showAsAction="never" />
</menu>
- فایل content_main.xml را باز کنید و المان recycler view را اضافه کنید .
content_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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context=".activity.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
</RelativeLayout>
- در نهایت MainActivity.java را باز کنید و کد را به صورت زیر تغییر دهید.
- اول نشست کاربر پیش از تنظیم محتوای view بررسی میشود .
- در دریافت توکن ثبت نام gcm، کاربر به موضوع global مشترک میشود. این به ما اجازه میدهد که یک نوتیفیکیشن به تمام کاربران از پنل مدیریت ارسال کنیم.
- fetchChatRooms() در متد onCreate() فراخوانی میشود که اطلاعات تمام اتاقهای چت را از سرور واکشی میکند. زمانی که اتاق چتها دریافت شدند، کاربر به طور خودکار به تمام موضوعات اتاقهای چت مشترک میشود. بنابراین او شروع به دریافت نوتیفیکیشن، هر جا که یک بحث فعال در یک اتاق چت در جریان باشد، میکند.
- دریافت کنندهی برودکست ثبت نام نشده / ثبت نام شده در متدهای onResume() / onPause()
- دریافت کنندهی برودکست هر جا که یک پیام نوتیفیکیشن در جایی که متد handlePushNotification() صدا زده شود دریافت بشود، شلیک خواهد شد
- handlePushNotification() پوش نوتیفیکیشن با بروزرسانی آیتمهای recycler view را با بروزرسانی last message و unread message count مدیریت میکند. با ادامه مقاله ساخت اپلیکیشن فایربیس و PHP همراه ما باشید.
MainActivity.java
package info.tik4.gcm.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import info.tik4.gcm.R;
import info.tik4.gcm.adapter.ChatRoomsAdapter;
import info.tik4.gcm.app.Config;
import info.tik4.gcm.app.EndPoints;
import info.tik4.gcm.app.MyApplication;
import info.tik4.gcm.gcm.GcmIntentService;
import info.tik4.gcm.gcm.NotificationUtils;
import info.tik4.gcm.helper.SimpleDividerItemDecoration;
import info.tik4.gcm.model.ChatRoom;
import info.tik4.gcm.model.Message;
public class MainActivity extends AppCompatActivity {
private String TAG = MainActivity.class.getSimpleName();
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private BroadcastReceiver mRegistrationBroadcastReceiver;
private ArrayList<ChatRoom> chatRoomArrayList;
private ChatRoomsAdapter mAdapter;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Check for login session. If not logged in launch
* login activity
* */
if (MyApplication.getInstance().getPrefManager().getUser() == null) {
launchLoginActivity();
}
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
/**
* Broadcast receiver calls in two scenarios
* 1. gcm registration is completed
* 2. when new push notification is received
* */
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// checking for type intent filter
if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) {
// gcm successfully registered
// now subscribe to `global` topic to receive app wide notifications
subscribeToGlobalTopic();
} else if (intent.getAction().equals(Config.SENT_TOKEN_TO_SERVER)) {
// gcm registration id is stored in our server's MySQL
Log.e(TAG, "GCM registration id is sent to our server");
} else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
// new push notification is received
handlePushNotification(intent);
}
}
};
chatRoomArrayList = new ArrayList<>();
mAdapter = new ChatRoomsAdapter(this, chatRoomArrayList);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(
getApplicationContext()
));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
recyclerView.addOnItemTouchListener(new ChatRoomsAdapter.RecyclerTouchListener(getApplicationContext(), recyclerView, new ChatRoomsAdapter.ClickListener() {
@Override
public void onClick(View view, int position) {
// when chat is clicked, launch full chat thread activity
ChatRoom chatRoom = chatRoomArrayList.get(position);
Intent intent = new Intent(MainActivity.this, ChatRoomActivity.class);
intent.putExtra("chat_room_id", chatRoom.getId());
intent.putExtra("name", chatRoom.getName());
startActivity(intent);
}
@Override
public void onLongClick(View view, int position) {
}
}));
/**
* Always check for google play services availability before
* proceeding further with GCM
* */
if (checkPlayServices()) {
registerGCM();
fetchChatRooms();
}
}
/**
* Handles new push notification
*/
private void handlePushNotification(Intent intent) {
int type = intent.getIntExtra("type", -1);
// if the push is of chat room message
// simply update the UI unread messages count
if (type == Config.PUSH_TYPE_CHATROOM) {
Message message = (Message) intent.getSerializableExtra("message");
String chatRoomId = intent.getStringExtra("chat_room_id");
if (message != null && chatRoomId != null) {
updateRow(chatRoomId, message);
}
} else if (type == Config.PUSH_TYPE_USER) {
// push belongs to user alone
// just showing the message in a toast
Message message = (Message) intent.getSerializableExtra("message");
Toast.makeText(getApplicationContext(), "New push: " + message.getMessage(), Toast.LENGTH_LONG).show();
}
}
/**
* Updates the chat list unread count and the last message
*/
private void updateRow(String chatRoomId, Message message) {
for (ChatRoom cr : chatRoomArrayList) {
if (cr.getId().equals(chatRoomId)) {
int index = chatRoomArrayList.indexOf(cr);
cr.setLastMessage(message.getMessage());
cr.setUnreadCount(cr.getUnreadCount() + 1);
chatRoomArrayList.remove(index);
chatRoomArrayList.add(index, cr);
break;
}
}
mAdapter.notifyDataSetChanged();
}
/**
* fetching the chat rooms by making http call
*/
private void fetchChatRooms() {
StringRequest strReq = new StringRequest(Request.Method.GET,
EndPoints.CHAT_ROOMS, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e(TAG, "response: " + response);
try {
JSONObject obj = new JSONObject(response);
// check for error flag
if (obj.getBoolean("error") == false) {
JSONArray chatRoomsArray = obj.getJSONArray("chat_rooms");
for (int i = 0; i < chatRoomsArray.length(); i++) {
JSONObject chatRoomsObj = (JSONObject) chatRoomsArray.get(i);
ChatRoom cr = new ChatRoom();
cr.setId(chatRoomsObj.getString("chat_room_id"));
cr.setName(chatRoomsObj.getString("name"));
cr.setLastMessage("");
cr.setUnreadCount(0);
cr.setTimestamp(chatRoomsObj.getString("created_at"));
chatRoomArrayList.add(cr);
}
} else {
// error in fetching chat rooms
Toast.makeText(getApplicationContext(), "" + obj.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "Json parse error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
mAdapter.notifyDataSetChanged();
// subscribing to all chat room topics
subscribeToAllTopics();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, "Volley error: " + error.getMessage() + ", code: " + networkResponse);
Toast.makeText(getApplicationContext(), "Volley error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
//Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
// subscribing to global topic
private void subscribeToGlobalTopic() {
Intent intent = new Intent(this, GcmIntentService.class);
intent.putExtra(GcmIntentService.KEY, GcmIntentService.SUBSCRIBE);
intent.putExtra(GcmIntentService.TOPIC, Config.TOPIC_GLOBAL);
startService(intent);
}
// Subscribing to all chat room topics
// each topic name starts with `topic_` followed by the ID of the chat room
// Ex: topic_1, topic_2
private void subscribeToAllTopics() {
for (ChatRoom cr : chatRoomArrayList) {
Intent intent = new Intent(this, GcmIntentService.class);
intent.putExtra(GcmIntentService.KEY, GcmIntentService.SUBSCRIBE);
intent.putExtra(GcmIntentService.TOPIC, "topic_" + cr.getId());
startService(intent);
}
}
private void launchLoginActivity() {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
@Override
protected void onResume() {
super.onResume();
// register GCM registration complete receiver
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(Config.REGISTRATION_COMPLETE));
// register new push message receiver
// by doing this, the activity will be notified each time a new message arrives
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(Config.PUSH_NOTIFICATION));
// clearing the notification tray
NotificationUtils.clearNotifications();
}
@Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
// starting the service to register with GCM
private void registerGCM() {
Intent intent = new Intent(this, GcmIntentService.class);
intent.putExtra("key", "register");
startService(intent);
}
private boolean checkPlayServices() {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
.show();
} else {
Log.i(TAG, "This device is not supported. Google Play Services not installed!");
Toast.makeText(getApplicationContext(), "This device is not supported. Google Play Services not installed!", Toast.LENGTH_LONG).show();
finish();
}
return false;
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_logout:
MyApplication.getInstance().logout();
break;
}
return super.onOptionsItemSelected(menuItem);
}
}
حالا برنامه را در هر دوی دستگاه/شبیه ساز برای تست پوش نوتیفیکیشن نصب کنید. شما همچنین میتوانید از برنامهی پنل مدیریتی سرور با مراجعه به آدرس http://localhost/gcm_chat استفاده کنید و یک پیام در اتاق چت تایپ کنید.
اضافه کردن تک ریسمان اتاق چت
صفحهی تک ریسمان اتاق چت شامل پیامهایی از کاربران مختلف است که به چپ و راست در صفحه تراز شده اند. تمام پیامهایی به کاربران دیگر تعلق دارند به سمت چپ تراز شده اند. پیامهای متعلق به کاربر جاری لاگ این شده به سمت راست تراز خواهد شد.
این ترازبندی میتواند با استفاده از یک recycler view و دو لایهی مختلف xml برای خود و دیگرپیامها انجام شود. با ادامه مقاله ساخت اپلیکیشن چت با فایربیس همراه ما باشید.
- یک فایل xml به نام bg_bubble_white.xml درون پوشهی drawable ایجاد کنید. این layout یک پسزمینه با گوشهی گرد به پیامهای دیگران اضافه میکند.
bg_bubble_white.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- view background color -->
<solid
android:color="@android:color/white" >
</solid>
<!-- If you want to add some padding -->
<padding
android:left="10dp"
android:top="4dp"
android:right="10dp"
android:bottom="4dp" >
</padding>
<!-- Here is the corner radius -->
<corners
android:radius="5dp" >
</corners>
</shape>
- یک فایل xml به نام bg_bubble_gray.xml درون پوشهی drawable ایجاد کنید. این layout یک پس زمینه با گوشهی گرد به پیامهای خودی اضافه میکند.
bg_bubble_gray.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- view background color -->
<solid
android:color="@color/bg_bubble_self" >
</solid>
<!-- If you want to add some padding -->
<padding
android:left="10dp"
android:top="4dp"
android:right="10dp"
android:bottom="4dp" >
</padding>
<!-- Here is the corner radius -->
<corners
android:radius="5dp" >
</corners>
</shape>
- دو لایوت xml به نامهای chat_item_self.xml و chat_item_other.xml برای رندر کردن هر دو پیامهای خودی و پیامهای دیگران ایجاد کنید.
chat_item_self.xml
<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:gravity="right"
android:orientation="horizontal"
android:paddingBottom="5dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:textIsSelectable="true"
android:background="@drawable/bg_bubble_gray"
android:textSize="14dp" />
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/message"
android:layout_below="@id/message"
android:layout_marginBottom="25dp"
android:padding="10dp"
android:textSize="10dp" />
</RelativeLayout>
chat_item_other.xml
<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:orientation="horizontal"
android:paddingTop="5dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dp"
android:textIsSelectable="true"
android:background="@drawable/bg_bubble_white"
android:textSize="14dp" />
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/message"
android:layout_marginBottom="25dp"
android:layout_marginLeft="10dp"
android:paddingLeft="10dp"
android:paddingTop="6dp"
android:textSize="10dp" />
</RelativeLayout>
- یک کلاس به نام ChatRoomThreadAdapter.java زیر پکیج adapter ایجاد کنید.
ChatRoomThreadAdapter.java
This adapter class identifies the current logged in user messages by user id and align the messages left or right by inflating two different xml layouts.
package info.tik4.gcm.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.TextView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import info.tik4.gcm.R;
import info.tik4.gcm.model.Message;
public class ChatRoomThreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static String TAG = ChatRoomThreadAdapter.class.getSimpleName();
private String userId;
private int SELF = 100;
private static String today;
private Context mContext;
private ArrayList<Message> messageArrayList;
public class ViewHolder extends RecyclerView.ViewHolder {
TextView message, timestamp;
public ViewHolder(View view) {
super(view);
message = (TextView) itemView.findViewById(R.id.message);
timestamp = (TextView) itemView.findViewById(R.id.timestamp);
}
}
public ChatRoomThreadAdapter(Context mContext, ArrayList<Message> messageArrayList, String userId) {
this.mContext = mContext;
this.messageArrayList = messageArrayList;
this.userId = userId;
Calendar calendar = Calendar.getInstance();
today = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
// view type is to identify where to render the chat message
// left or right
if (viewType == SELF) {
// self message
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item_self, parent, false);
} else {
// others message
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.chat_item_other, parent, false);
}
return new ViewHolder(itemView);
}
@Override
public int getItemViewType(int position) {
Message message = messageArrayList.get(position);
if (message.getUser().getId().equals(userId)) {
return SELF;
}
return position;
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
Message message = messageArrayList.get(position);
((ViewHolder) holder).message.setText(message.getMessage());
String timestamp = getTimeStamp(message.getCreatedAt());
if (message.getUser().getName() != null)
timestamp = message.getUser().getName() + ", " + timestamp;
((ViewHolder) holder).timestamp.setText(timestamp);
}
@Override
public int getItemCount() {
return messageArrayList.size();
}
public static String getTimeStamp(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestamp = "";
today = today.length() < 2 ? "0" + today : today;
try {
Date date = format.parse(dateStr);
SimpleDateFormat todayFormat = new SimpleDateFormat("dd");
String dateToday = todayFormat.format(date);
format = dateToday.equals(today) ? new SimpleDateFormat("hh:mm a") : new SimpleDateFormat("dd LLL, hh:mm a");
String date1 = format.format(date);
timestamp = date1.toString();
} catch (ParseException e) {
e.printStackTrace();
}
return timestamp;
}
}
- یک کلاس اکتیویتی جدید به نام ChatRoomActivity.java با راست کلیک بر activity ⇒ New ⇒ Activity ⇒ Blank Activity ایجاد کنید. این دو فایل لایوت با نامهای content_chat_room.xmlو activity_chat_room.xml ایجاد میکند.
activity_chat_room.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".activity.ChatRoomActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_chat_room" />
</android.support.design.widget.CoordinatorLayout>
- content_chat_room.xml را باز کنید و کد زیر را به آن اضافه کنید. اینجا ما recycler view و یک آیتم EditText را برای وارد کردن پیام جدید در اتاق چت اضافه کردهایم.
content_chat_room.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".activity.ChatRoomActivity"
tools:showIn="@layout/activity_chat_room">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:scrollbars="vertical" />
<LinearLayout
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:weightSum="4">
<EditText android:id="@+id/message"
android:layout_width="0dp"
android:hint="Enter message"
android:paddingLeft="10dp"
android:background="@null"
android:layout_marginRight="10dp"
android:layout_marginLeft="16dp"
android:lines="1"
android:layout_height="wrap_content"
android:layout_weight="3" />
<Button android:id="@+id/btn_send"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:text="SEND"
android:textSize="16dp"
android:textColor="@color/colorPrimary" />
</LinearLayout>
</RelativeLayout>
- ChatRoomActivity.java را باز کنید و کد را به صورت زیر تغییر دهید.
- متد fetchChatThread() تمام پیامهای ردوبدل شدهی قبلی را واکشی میکند به اتاق چت.
- دریافت کنندهی برودکست برای PUSH_NOTIFICATION در متد onResume() ثبت شده است
- متد handlePushNotification() پیام پوش را مدیریت میکند و به لیست آرایهی adapter ضمیمه میکند. به محض فراخوانی notifyDataSetChanged() ، پیام جدید در ریسمان مباحثه نمایش داده میشود.
- پیامهای خودی و دیگران با مقایسهی user id در پیام پوش با idی کاربری که در حال حاضر لاگ این است، به سمت چپ یا راست تراز میشوند
- متد sendMessage() یک پیام جدید برای پست در اتاق چت، به سرور میفرستد. در سرور پیام برای برودکست به دستگاههای دیگری که به موضوع اتاق چت مشترک شده اند، به سرور gcm ارسال میشود. با ادامه مقاله ساخت اپلیکیشن چت با فایربیس باما همراه باشید.
ChatRoomActivity.java
package info.tik4.gcm.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import info.tik4.gcm.R;
import info.tik4.gcm.adapter.ChatRoomThreadAdapter;
import info.tik4.gcm.app.Config;
import info.tik4.gcm.app.EndPoints;
import info.tik4.gcm.app.MyApplication;
import info.tik4.gcm.gcm.NotificationUtils;
import info.tik4.gcm.model.Message;
import info.tik4.gcm.model.User;
public class ChatRoomActivity extends AppCompatActivity {
private String TAG = ChatRoomActivity.class.getSimpleName();
private String chatRoomId;
private RecyclerView recyclerView;
private ChatRoomThreadAdapter mAdapter;
private ArrayList<Message> messageArrayList;
private BroadcastReceiver mRegistrationBroadcastReceiver;
private EditText inputMessage;
private Button btnSend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_room);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
inputMessage = (EditText) findViewById(R.id.message);
btnSend = (Button) findViewById(R.id.btn_send);
Intent intent = getIntent();
chatRoomId = intent.getStringExtra("chat_room_id");
String title = intent.getStringExtra("name");
getSupportActionBar().setTitle(title);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (chatRoomId == null) {
Toast.makeText(getApplicationContext(), "Chat room not found!", Toast.LENGTH_SHORT).show();
finish();
}
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
messageArrayList = new ArrayList<>();
// self user id is to identify the message owner
String selfUserId = MyApplication.getInstance().getPrefManager().getUser().getId();
mAdapter = new ChatRoomThreadAdapter(this, messageArrayList, selfUserId);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
// new push message is received
handlePushNotification(intent);
}
}
};
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessage();
}
});
fetchChatThread();
}
@Override
protected void onResume() {
super.onResume();
// registering the receiver for new notification
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(Config.PUSH_NOTIFICATION));
NotificationUtils.clearNotifications();
}
@Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
/**
* Handling new push message, will add the message to
* recycler view and scroll it to bottom
* */
private void handlePushNotification(Intent intent) {
Message message = (Message) intent.getSerializableExtra("message");
String chatRoomId = intent.getStringExtra("chat_room_id");
if (message != null && chatRoomId != null) {
messageArrayList.add(message);
mAdapter.notifyDataSetChanged();
if (mAdapter.getItemCount() > 1) {
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, mAdapter.getItemCount() - 1);
}
}
}
/**
* Posting a new message in chat room
* will make an http call to our server. Our server again sends the message
* to all the devices as push notification
* */
private void sendMessage() {
final String message = this.inputMessage.getText().toString().trim();
if (TextUtils.isEmpty(message)) {
Toast.makeText(getApplicationContext(), "Enter a message", Toast.LENGTH_SHORT).show();
return;
}
String endPoint = EndPoints.CHAT_ROOM_MESSAGE.replace("_ID_", chatRoomId);
Log.e(TAG, "endpoint: " + endPoint);
this.inputMessage.setText("");
StringRequest strReq = new StringRequest(Request.Method.POST,
endPoint, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e(TAG, "response: " + response);
try {
JSONObject obj = new JSONObject(response);
// check for error
if (obj.getBoolean("error") == false) {
JSONObject commentObj = obj.getJSONObject("message");
String commentId = commentObj.getString("message_id");
String commentText = commentObj.getString("message");
String createdAt = commentObj.getString("created_at");
JSONObject userObj = obj.getJSONObject("user");
String userId = userObj.getString("user_id");
String userName = userObj.getString("name");
User user = new User(userId, userName, null);
Message message = new Message();
message.setId(commentId);
message.setMessage(commentText);
message.setCreatedAt(createdAt);
message.setUser(user);
messageArrayList.add(message);
mAdapter.notifyDataSetChanged();
if (mAdapter.getItemCount() > 1) {
// scrolling to bottom of the recycler view
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, mAdapter.getItemCount() - 1);
}
} else {
Toast.makeText(getApplicationContext(), "" + obj.getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "json parse error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, "Volley error: " + error.getMessage() + ", code: " + networkResponse);
Toast.makeText(getApplicationContext(), "Volley error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
inputMessage.setText(message);
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("user_id", MyApplication.getInstance().getPrefManager().getUser().getId());
params.put("message", message);
Log.e(TAG, "Params: " + params.toString());
return params;
};
};
// disabling retry policy so that it won't make
// multiple http calls
int socketTimeout = 0;
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
strReq.setRetryPolicy(policy);
//Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
/**
* Fetching all the messages of a single chat room
* */
private void fetchChatThread() {
String endPoint = EndPoints.CHAT_THREAD.replace("_ID_", chatRoomId);
Log.e(TAG, "endPoint: " + endPoint);
StringRequest strReq = new StringRequest(Request.Method.GET,
endPoint, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e(TAG, "response: " + response);
try {
JSONObject obj = new JSONObject(response);
// check for error
if (obj.getBoolean("error") == false) {
JSONArray commentsObj = obj.getJSONArray("messages");
for (int i = 0; i < commentsObj.length(); i++) {
JSONObject commentObj = (JSONObject) commentsObj.get(i);
String commentId = commentObj.getString("message_id");
String commentText = commentObj.getString("message");
String createdAt = commentObj.getString("created_at");
JSONObject userObj = commentObj.getJSONObject("user");
String userId = userObj.getString("user_id");
String userName = userObj.getString("username");
User user = new User(userId, userName, null);
Message message = new Message();
message.setId(commentId);
message.setMessage(commentText);
message.setCreatedAt(createdAt);
message.setUser(user);
messageArrayList.add(message);
}
mAdapter.notifyDataSetChanged();
if (mAdapter.getItemCount() > 1) {
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, mAdapter.getItemCount() - 1);
}
} else {
Toast.makeText(getApplicationContext(), "" + obj.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
Toast.makeText(getApplicationContext(), "json parse error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, "Volley error: " + error.getMessage() + ", code: " + networkResponse);
Toast.makeText(getApplicationContext(), "Volley error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
//Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
}
حالا برنامه را برروی دو دستگاه مختلف نصب کنید و ارسال یک پیام در اتاق چت را امتحان کنید. دستگاه دیگر باید شروع به دریافت پیام کند. شما همچنین میتوانید با استفاده از پنل مدیریت http://localhost/gcm_chat اقدام به ارسال پیام به هر دو دستگاه کنید. و همچنین شما میتوانید برنامه را به اندازهی حداقل برسانید(minimize) و پیام جدید را در نوار اعلانات(notification bar) بررسی کنید.
امیدواریم از قسمت سوم مقاله ساخت اپلیکیشن فایربیس و PHP بهره کافی را برده باشید. امیدواریم در ادامه با مطالب ساخت اپلیکیشن چت با فایربیس همراه ما باشید.