How to build a Complete Messenger in 10 minutes using Laravel, Retrofit and MQTT CHAT Android Library.

Server Side (PHP)

In server side we will use LARAVEL PHP Framework to build API and to connect to Mysql Database. So let’s begin by creating new Laravel application using PHP Composer tool.

composer create-project laravel/laravel android-tuto
composer require med_aboub/mqttchat-php-sdk -W
php artisan make:controller UsersController
class UsersController extends Controller
{

public function add(Request $request){
try{
DB::beginTransaction();
$post=json_decode($request->getContent(),true);
$user= \App\Models\Users::create($post);
$users=new \telifoun\mqttchat\users();
$post_array=array("userid"=>$user["userid"],
"name"=>$user["name"],
"surname"=>$user["surname"],
"profile_link"=>"",
"avatar_link"=>"",
"gender"=>0);
$result=$users->add($post_array);
if($result["ok"]){
DB::commit();
return response()->json(["ok"=>true,"response"=>$result["data"]["userid"]]);
}else{
DB::rollBack();
return response()->json($result["error"],500);
}
}catch(\Exception $ex){
DB::rollBack();
return response()->json($ex->getMessage(),500);
}
}
public function login(Request $request){
try{
$user=\App\Models\Users::where("email","=",$request->get("email"))
->where("password","=",$request->get("password"))
->first();
if($user){
return response()->json(["ok"=>true,"response"=>$user->toArray()]);
}else{
return response()->json(["ok"=>false,"error"=>"Invalid Credentails"]);
}

} catch (Exception $ex) {
return response()->json($ex->getMessage(),500);
}
}
}
Route::post('/users',[App\Http\Controllers\UsersController::class,'add']);
Route::get('/login',[App\Http\Controllers\UsersController::class,'login']);
class Users extends Model
{
use HasFactory;
protected $table = 'users';
protected $primaryKey = 'userid';
public $timestamps = false;
protected $fillable = [
'name',
'surname',
'email',
'password'
];
}
CREATE TABLE `users` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`surname` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`userid`)
)

Android APPLICATION

Project SETUP

Create a new project in Android Studio from File ⇒ New Project by filling the required details.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.test123">
<application
android:name=".mApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.RegisterActivity" />
<activity android:name=".activity.MainActivity" />
<activity android:name=".activity.LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
android {
compileSdk 32

defaultConfig {
applicationId "com.app.tuto"
minSdk 16
targetSdk 32
versionCode 1
versionName "1.0"
multiDexEnabled true

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildFeatures{
dataBinding true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {

implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.telifoun.mqttchat:mqttchat-core:4.2.0'
implementation 'com.telifoun.mqttchat:mqttchat-gui:4.2.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
}
allprojects {
repositories {
maven {
url "https://mqttchat.jfrog.io/artifactory/libs-release-local"
credentials {
username = "mqttchat"
password = "telifoun"
}
}
}
}
public class User  {

@SerializedName("userid")
@Expose(serialize = false, deserialize = true)
private int userid;

@SerializedName("name")
@Expose
private String name;

@SerializedName("surname")
@Expose
private String surName;

@SerializedName("mail")
@Expose
private String email;

@SerializedName("password")
@Expose
private String password;

public User(int userid, String name, int surname, String email, String password) {
this.userid = userid;
this.name=name;
this.email=email;
this.password=password;
}
public class ApiRegisterResponse {public ApiRegisterResponse(Boolean ok, String error) {
this.ok = ok;
this.error = error;
}
@SerializedName("ok")
@Expose
private Boolean ok;

@SerializedName("error")
@Expose
private String error;

@SerializedName("response")
@Expose
private String response;

}
public class ApiLoginResponse {public ApiLoginResponse(Boolean ok, String error) {
this.ok = ok;
this.error = error;
}
@SerializedName("ok")
@Expose
private Boolean ok;


@SerializedName("error")
@Expose
private String error;

@SerializedName("response")
@Expose
private User user;
}
public class MqttChatResponse {

public MqttChatResponse(){
this.ok = Boolean.TRUE;
}
public MqttChatResponse(String error) {
this.ok = Boolean.FALSE;
this.error = error;
}
private Boolean ok;

private String error;

public Boolean getOk() {
return ok;
}

public String getError() {
return error;
}
}
interface APIService {

public interface APIService {

@POST(Config.BASE_URL + "users")
Call<ApiRegisterResponse> register(@Body User newUser);

@GET(Config.BASE_URL + "login")
Call<ApiLoginResponse> login(@Query("email") String email,
@Query("password") String password);
}
}
public class Config {
public static String REST_SERVER_URL="http://10.0.1.155";
public static final String BASE_URL ="/android-tuto/api";
}

MQTT CHAT initialisation

You should first get APP_ID and APP_SECRET from your MQTT CHAT admin panel.

public class mApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
new MqttChat.getBuilder()
.context(this.getApplicationContext())
.appIcon(R.mipmap.ic_launcher)
.domain("domain.com")
.appId("mqttchat-*******")
.appSecret("****************")
.debugMode(true)
.build();
}
}

ADD NEW USER

Add new package register under ui package then add:

  • New Activity RegisterActivity.java.
  • DataSource class RegisterDataSource.java.
  • ViewModel class RegisterViewModel.java.
public class RegisterActivity extends AppCompatActivity {
private ActivityRegisterBinding binding;
private RegisterViewModel mRegisterViewModel;
private ProgressDialog mProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= DataBindingUtil.setContentView(this, R.layout.activity_register);

mRegisterViewModel= new ViewModelProvider(this).get(RegisterViewModel.class);
mRegisterViewModel.getProgressDialog().observe(RegisterActivity.this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if(aBoolean){
mProgressDialog= ProgressDialog.show(RegisterActivity.this,"",
"Please Wait",true);
}else{
mProgressDialog.dismiss();
}
}
});
mRegisterViewModel.getRegisterResponse().observe(RegisterActivity.this, new Observer<ApiRegisterResponse>() {
@Override
public void onChanged(ApiRegisterResponse apiRegisterResponse) {
if(apiRegisterResponse.getOk()){
Toast.makeText(RegisterActivity.this,"Register Success ", Toast.LENGTH_LONG).show();
Intent i = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(i);
}else{
Toast.makeText(RegisterActivity.this, apiRegisterResponse.getError(), Toast.LENGTH_LONG).show();
}
}
});
binding.registerBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
mRegisterViewModel.register(new User(0,
binding.name.getText().toString(),
binding.surname.getText().toString(),
binding.email.getText().toString(),
binding.password.getText().toString()));

}
});
}
}
public class RegisterViewModel extends ViewModel {

private RegisterDataSource mDataSource;
private LiveData<Boolean> progressDialog;
private LiveData<ApiRegisterResponse> registerResponse;

public RegisterViewModel(){
mDataSource =new RegisterDataSource();
}


public void register(User user){
mDataSource.register(user);
}

public LiveData<Boolean> getProgressDialog() {
if(progressDialog==null){
progressDialog= mDataSource.getProgressDialog();
}
return progressDialog;
}

public LiveData<ApiRegisterResponse> getRegisterResponse() {
if(registerResponse==null){
registerResponse=mDataSource.getRegisterResponse();
}
return registerResponse;
}
}
public class RegisterDataSource {

private MutableLiveData<Boolean> progressDialog;
private MutableLiveData<ApiRegisterResponse> registerResponse;

public RegisterDataSource() {
this.progressDialog=new MutableLiveData<Boolean>();
this.registerResponse=new MutableLiveData<ApiRegisterResponse>();
}


public void register(User user){
APIService apiService= ServiceGenerator.createService(APIService.class);
getProgressDialog().postValue(true);
apiService.register(user).enqueue(new Callback<ApiRegisterResponse>() {
@Override
public void onResponse(Call<ApiRegisterResponse> call, Response<ApiRegisterResponse> response) {
getProgressDialog().postValue(false);
if(response.isSuccessful()) {
getRegisterResponse().postValue(response.body());
}else{
try {
getRegisterResponse().postValue(new ApiRegisterResponse(false,response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ApiRegisterResponse> call, Throwable t) {
getProgressDialog().postValue(false);
getRegisterResponse().postValue(new ApiRegisterResponse(false,t.getMessage()));
}
});
}

public MutableLiveData<Boolean> getProgressDialog() {
return progressDialog;
}

public MutableLiveData<ApiRegisterResponse> getRegisterResponse() {
return registerResponse;
}
}

USER LOGIN

Add new package login under ui package then add:

  • New Activity LoginActivity.java.
  • DataSource class LoginDataSource.java.
  • ViewModel class LoginViewModel.java.
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding binding;
private LoginViewModel mLoginViewModel;
private ProgressDialog mProgressDialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= DataBindingUtil.setContentView(this, R.layout.activity_login);

mLoginViewModel= new ViewModelProvider(this).get(LoginViewModel.class);
mLoginViewModel.getProgressDialog().observe(LoginActivity.this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if(aBoolean){
mProgressDialog= ProgressDialog.show(LoginActivity.this,"",
"Please Wait",true);
}else{
mProgressDialog.dismiss();
}
}
});
mLoginViewModel.getLoginResponse().observe(LoginActivity.this, new Observer<ApiLoginResponse>() {
@Override
public void onChanged(ApiLoginResponse apiLoginResponse) {
if(apiLoginResponse.getOk()){
mLoginViewModel.connectToChat(getApplication(),apiLoginResponse.getResponse().getUserId());
}else{
Toast.makeText(LoginActivity.this,apiLoginResponse.getError(), Toast.LENGTH_LONG).show();
}
}
});
mLoginViewModel.getMqttChatResponse().observe(LoginActivity.this, new Observer<MqttChatResponse>() {
@Override
public void onChanged(MqttChatResponse mqttchatResponse) {
if(mqttchatResponse.getOk()){
Intent i = new Intent(LoginActivity.this, MainActivity.class);
startActivity(i);
}else{
Toast.makeText(LoginActivity.this,mqttchatResponse.getError(), Toast.LENGTH_LONG).show();
}
}
});
binding.loginBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
mLoginViewModel.login(binding.email.getText().toString(),
binding.password.getText().toString());

}
});
binding.registerBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
Intent i=new Intent(LoginActivity.this,RegisterActivity.class);
startActivity(i);
}
});

}
}
  • To login user using HTTP Request.
  • To connect user to MQTT-CHAT .
public class LoginViewModel extends ViewModel{

private LoginDataSource mDataSource;
private LiveData<Boolean> progressDialog;
private LiveData<ApiLoginResponse> loginResponse;
private LiveData<MqttChatResponse> mqttChatResponse;

public LoginViewModel(){
mDataSource =new LoginDataSource();
}


public void login(String email,String password){
mDataSource.login(email,password);
}

public void connectToChat(Context ctx, int userid){
mDataSource.connect(ctx,userid);
}

public LiveData<Boolean> getProgressDialog() {
if(progressDialog==null){
progressDialog= mDataSource.getProgressDialog();
}
return progressDialog;
}

public LiveData<ApiLoginResponse> getLoginResponse() {
if(loginResponse==null){
loginResponse=mDataSource.getLoginResponse();
}
return loginResponse;
}

public LiveData<MqttChatResponse> getMqttChatResponse() {
if(mqttChatResponse==null){
mqttChatResponse=mDataSource.getMqttChatResponse();
}
return mqttChatResponse;
}
}
public class LoginDataSource {

private MutableLiveData<Boolean> progressDialog;
private MutableLiveData<ApiLoginResponse> loginResponse;
private MutableLiveData<MqttChatResponse> mqttChatResponse;

public LoginDataSource() {
this.progressDialog=new MutableLiveData<Boolean>();
this.loginResponse=new MutableLiveData<ApiLoginResponse>();
this.mqttChatResponse=new MutableLiveData<MqttChatResponse>();
}


public void login(String email,String password){
APIService apiService= ServiceGenerator.createService(APIService.class);
getProgressDialog().postValue(true);
apiService.login(email,password).enqueue(new Callback<ApiLoginResponse>() {
@Override
public void onResponse(Call<ApiLoginResponse> call, Response<ApiLoginResponse> response) {
getProgressDialog().postValue(false);
if(response.isSuccessful()) {
getLoginResponse().postValue(response.body());
}else{
try {
getLoginResponse().postValue(new ApiLoginResponse(false,response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ApiLoginResponse> call, Throwable t) {
getProgressDialog().postValue(false);
getLoginResponse().postValue(new ApiLoginResponse(false,t.getMessage()));
}
});
}

public void connect(Context ctx, int userid){
getProgressDialog().postValue(true);
MqttChat.getInstance().logIn(ctx, userid, new CallbackListener() {
@Override
public void onSuccess(Object o) {
MqttChat.getInstance().Connect(new CallbackListener() {
@Override
public void onSuccess(Object o) {
getProgressDialog().postValue(false);
mqttChatResponse.postValue(new MqttChatResponse());
}
@Override
public void onError(String error) {
getProgressDialog().postValue(false);
mqttChatResponse.postValue(new MqttChatResponse(error));
}
});
}
@Override
public void onError(String s) {
}
});
}

public MutableLiveData<Boolean> getProgressDialog() {
return progressDialog;
}

public MutableLiveData<ApiLoginResponse> getLoginResponse() {
return loginResponse;
}

public MutableLiveData<MqttChatResponse> getMqttChatResponse() {
return mqttChatResponse;
}
}

SHOW MESSENGER IN MainActivity

Add new package main under ui package then add:

  • New Activity MainActivity.java.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<FrameLayout
android:name=".gui.ui.fragments.mqttchat.MqttChatFragment"
android:id="@+id/mqttchatFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class MainActivity extends PresenceActivityA {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(R.id.mqttchatFragment,new MqttChatFragment(), "mqttchat").commit();
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store