Tutorial ini akan membahas tentang penerapan desain pattern / arsitektur Model View Presenter
pada aplikasi android dengan studi kasus aplikasi untuk Register dan Login menggunakan PHP dan MySQL.
Tutorial ini dibagi menjadi 3 bagian, yaitu Database, EndPoint, dan Aplikasi Android.
- https://github.com/irfankhoirul/LoginRegisterMVP
- https://github.com/irfankhoirul/LoginRegisterMVP-Service
PENDAHULUAN
Model View Presenter adalah arsitektur untuk menerapkan prinsip separation of concern pada
aplikasi Android, sama seperti MVC pada aplikasi-aplikasi Web dan MVVM pada aplikasi .Net. Pemilihan
MVP dibanding MVC adalah karena MVP lebih modular karena setiap elemennya (Model / View /
Presenter) hanya memiliki satu fungsi / single purpose. Perbedaan paling mendasar MVC dan MVP adalah
pada View, dimana View MVC dapat mengakses Model dan sebaliknya, sedangkan View pada MVP tidak
dapat mengakses model, namun harus melalui perentara Presenter. Sementara itu pemilihan MVP
dibanding MVVM adalah karena arsitektur MVP lebih sederhana karena pada dasarnya MVVM digunakan
tidak hanya untuk menerapkan separation of concern, tetapi juga untuk menerapkan two way data
binding pada aplikasi Windows Prsentation Foundation .Net.
1
Penerapan MVP pada Android adalah sebagai berikut :
2
DATABASE
- MySQL server
- Aplikasi pengelola MySQL (MySQL Workbench / PHPMyAdmin/ etc)
Steps :
use android_api;
3
WEB SERVICE
- Laravel 5.4
Setelah database siap, buat web service untuk menjembatani aplikasi Android dan database server. Web
service yang dibuat terdiri dari 3 EndPoint, register, login, dan logout.
Steps :
1. Install Laravel
Jika melakukan testing aplikasi Android menggunakan Smartphone, pastikan PC dan smartphone
berada dalam satu jaringan. Kemudian jalankan Laravel dengan mengeset host sesuai IP address
PC.
3. Configurasi Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=android_api
DB_USERNAME=root
DB_PASSWORD=
4. Buat Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
4
const CODE_SUCCESS = 1;
const CODE_ERROR = 0;
const RESULT_REGISTRATION_SUCCESS = "Registration success";
const RESULT_REGISTRATION_FAILED = "Registration success";
const RESULT_WRONG_PASSWORD = "Password didn't match";
const RESULT_LOGIN_FAILED = "Login failed";
const RESULT_LOGIN_SUCCESS = "Login success";
const RESULT_LOGOUT_FAILED = "Logout failed";
const RESULT_LOGOUT_SUCCESS = "Logout success";
const RESULT_USER_NOT_FOUND = "Account not found";
const RESULT_TOKEN_NOT_FOUND = "Auth token not found";
$salt = str_random(32);
$id = DB::table($this->table)->insertGetId(
['name' => $userData['name'],
'email' => $userData['email'],
'gender' => $userData['gender'],
'encrypted_password' => hash('sha256',
$salt . hash('md5', $userData['password'] . $salt)),
'salt' => $salt,
'is_login' => FALSE,
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s")
]
);
return array(self::CODE_SUCCESS,
self::RESULT_REGISTRATION_SUCCESS, $id);
} catch (QueryException $ex) {
return array(self::CODE_ERROR,
self::RESULT_REGISTRATION_FAILED, $ex->getMessage());
}
}
5
public function login($userData)
{
try {
$user = NULL;
if ($userData['email'] != NULL) {
$user = DB::table($this->table)->where('email',
$userData['email'])->first();
if (empty($user)) {
return array(self::CODE_ERROR,
self::RESULT_USER_NOT_FOUND, NULL, NULL);
}
}
if ($result == self::CODE_SUCCESS) {
return array(self::CODE_SUCCESS,
self::RESULT_LOGOUT_SUCCESS, NULL);
} else {
return array(self::CODE_ERROR,
self::RESULT_TOKEN_NOT_FOUND, NULL);
}
}
} catch (QueryException $ex) {
return array(self::CODE_ERROR,
self::RESULT_LOGOUT_FAILED, $ex->getMessage());
}
}
5. Buat Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use Response;
const CODE_SUCCESS = 1;
const CODE_ERROR = 0;
7
}
$result['data'] = $data;
header('Content-Type: application/json');
echo(json_encode($result, JSON_PRETTY_PRINT));
die;
}
protected function
returnJsonErrorDataNotValid($errorMessage)
{
$result = array();
$result['code'] = self::CODE_ERROR;
$result['message'] = "Data yang dikirim tidak valid";
if (config('app.debug')) {
$result['debugMessage'] = $errorMessage;
}
header('Content-Type: application/json');
echo(json_encode($result, JSON_PRETTY_PRINT));
die;
}
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use App\Http\Requests;
use Validator;
/**
* Class UserController
* @package TATravel\Http\Controllers
*/
class UserController extends BaseController
{
8
'name' => 'required|max:50',
'email' => 'required|email|max:100',
'gender' => 'required|max:1',
'password' => 'required'
]);
if ($validator->fails()) {
$this->returnJsonErrorDataNotValid($validator-
>errors());
}
$userData['name'] = $request->request->get('name');
$userData['email'] = $request->request->get('email');
$userData['gender'] = $request->request->get('gender');
$userData['password'] = $request->request-
>get('password');
if ($validator->fails()) {
$this->returnJsonErrorDataNotValid($validator-
>errors());
}
$userData['email'] = $request->request->get('email');
$userData['password'] = $request->request-
>get('password');
9
$user = new User();
list($status, $message, $technicalMessage, $data) =
$user->login($userData);
$this->returnJson($status, $message, $technicalMessage,
$data);
}
if ($validator->fails()) {
$this->returnJsonErrorDataNotValid($validator-
>errors());
}
$token = $request->request->get('token');
$user = new User();
list($status, $message, $technicalMessage) = $user-
>logout($token);
$this->returnJson($status, $message, $technicalMessage,
null);
}
6. Menambahkan EndPoint
Tambahkan 3 baris berikut pada file routes/api.php yang sudah tersedia.
10
11
ANDROID
Setelah web service dibuat, selanjutnya adalah membuat aplikasi Android yang akan menggunakan web
service tersebut. Aplikasi Android dibuat dengan menerapkan arsitektur Model View Presenter dengan
memnfaatkan library MVPCore.
Steps :
12
2. Tentukan SDK Minimum, pada contoh ini adalah Jelly Bean 4.1
13
4. Setelah project dibuat, tambahkan dependency dan repository pada file berikut :
a. build.gradle (Module level)
dependencies {
// ... (dependensi lain)
// MVPCore
compile 'com.github.irfankhoirul:MVPCore:0.1.5'
// Gson
compile 'com.google.code.gson:gson:2.7'
// Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
// Butterknife
compile 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-
compiler:8.4.0'
// Form Validation
compile 'com.basgeekball:awesome-validation:2.0'
allprojects {
repositories {
maven { url "https://jitpack.io" }
// ... (repository lain)
}
}
5. Struktur package
14
Berikut adalah struktur package yang digunakan pada tutorial ini. Package terluar adalah nama
package aplikasi yang dibuat. Di dalamnya terdapat 2 package, yaitu data dan modul. Package
data digunakan untuk menyimpan model. Package modul digunakan untuk menyimpan modul.
Di dalam package model terdapat 2 package, yaitu pojo dan source. Package pojo digunakan
untuk menyimpan kelas-kelas POJO (Plain Old Java Object), yaitu kelas wrapper dari setiap tabel
yang terdapat dalam database. Sedangkan package source digunakan untuk menyimpan kelas
yang digunakan untuk melakukan data access, baik itu data local (shared preference, local
database, maupun lokal file pada internal maupun external storage) ataupun data remote
(database server).
Sementara itu, package modul berisi modul-modul yang terdapat dalam aplikasi. Cara mudah
untuk menentukan modul adalah berdasarkan user interface / view (activity / fragment). Setiap
package modul sebaiknya hanya terdiri dari 1 view. Setiap 1 view hanya memiliki 1 presenter
karena relationship dari view dan presenter adalah one to one.
B. Membuat Model
1. Membuat kelas User
Kelas User adalah wrapper dari tabel Users yang terdapat dalam database. Buat kelas User di
dalam package pojo.
package com.irfankhoirul.loginregistermvp.data.pojo;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.irfankhoirul.mvp_core.base.BasePojo;
15
@SerializedName("name")
@Expose
private String name;
@SerializedName("email")
@Expose
private String email;
@SerializedName("auth_token")
@Expose
private String authToken;
@SerializedName("gender")
@Expose
private String gender;
16
public void setPassword(String password) {
this.password = password;
}
package com.irfankhoirul.loginregistermvp.data.source.remote;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import com.irfankhoirul.mvp_core.data.DataResult;
import java.util.Map;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
@FormUrlEncoded
@POST(REGISTER_END_POINT)
Call<DataResult> register(@FieldMap Map<String, String>
param);
@FormUrlEncoded
@POST(LOGIN_END_POINT)
Call<DataResult<User>> login(@FieldMap Map<String, String>
param);
17
@FormUrlEncoded
@POST(LOGOUT_END_POINT)
Call<DataResult> logout(@FieldMap Map<String, String>
param);
package com.irfankhoirul.loginregistermvp.data.source.remote;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;
package com.irfankhoirul.loginregistermvp.data.source.remote;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import com.irfankhoirul.mvp_core.base.BaseRemoteRepository;
import com.irfankhoirul.mvp_core.data.DataResult;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;
import java.util.HashMap;
import java.util.Map;
import retrofit2.Call;
18
// Localhost PC
private static final String BASE_URL =
"http://10.0.2.2:8000/api/";
@Override
protected String setBaseUrl() {
return BASE_URL;
}
@Override
protected void setEndPoint() {
endPoint = retrofit.create(UserEndPoints.class);
}
@Override
protected boolean enableLogging() {
return true;
}
@Override
public void register(User user, RequestResponseListener
listener) {
Map<String, String> params = new HashMap<>();
params.put("name", user.getName());
params.put("email", user.getEmail());
params.put("password", user.getPassword());
params.put("gender", user.getGender());
Call<DataResult> call = endPoint.register(params);
execute(call, listener);
}
@Override
public void login(String email, String password,
RequestResponseListener<User> listener) {
Map<String, String> params = new HashMap<>();
params.put("email", email);
params.put("password", password);
Call<DataResult<User>> call = endPoint.login(params);
execute(call, listener);
}
@Override
public void logout(String token, RequestResponseListener
listener) {
Map<String, String> params = new HashMap<>();
params.put("token", token);
19
Call<DataResult> call = endPoint.logout(params);
execute(call, listener);
}
}
package com.irfankhoirul.loginregistermvp.data.source.local;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
User getSessionData();
void destroy();
}
package com.irfankhoirul.loginregistermvp.data.source.local;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
20
Context.MODE_PRIVATE);
}
@Override
public User initialize(User sessionData) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(SESSION_DATA_KEY, new
Gson().toJson(sessionData));
editor.apply();
String sessionDataJson =
sharedPref.getString(SESSION_DATA_KEY, null);
return new Gson().fromJson(sessionDataJson,
User.class);
}
@Override
public User getSessionData() {
String sessionDataJson =
sharedPref.getString(SESSION_DATA_KEY, null);
if (sessionDataJson != null) {
return new Gson().fromJson(sessionDataJson,
User.class);
}
return null;
}
@Override
public void setSessionData(User newSessionData) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(SESSION_DATA_KEY, new
Gson().toJson(newSessionData));
editor.apply();
}
@Override
public void destroy() {
sharedPref.edit().clear().apply();
}
21
22
Buat kelas RegisterActivity pada package register melalui menu New->Activity->EmptyActivity.
Activity ini tidak memerlukan LayoutFile, jadi pastikan tidak ada centang pada Generate Layout
File.
package com.irfankhoirul.loginregistermvp.modul.register;
import
com.irfankhoirul.mvp_core.base.BaseFragmentHolderActivity;
@Override
protected void initializeFragment() {
RegisterFragment registerFragment = new
RegisterFragment();
setCurrentFragment(registerFragment, false);
}
23
}
package com.irfankhoirul.loginregistermvp.modul.register;
import com.irfankhoirul.mvp_core.base.BasePresenter;
import com.irfankhoirul.mvp_core.base.BaseView;
<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:padding="16dp"
tools:context="com.irfankhoirul.loginregistermvp.modul.register
.RegisterFragment">
<android.support.design.widget.TextInputLayout
android:id="@+id/til_name"
android:layout_width="match_parent"
android:layout_height="wrap_content">
24
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nama"
android:inputType="textCapWords" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/til_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/til_name">
<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/til_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/til_email"
app:passwordToggleEnabled="true">
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<RadioGroup
android:id="@+id/bt_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/til_password"
android:layout_marginTop="16dp"
25
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_male"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="I'm Male" />
<RadioButton
android:id="@+id/rb_female"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="I'm Female" />
</RadioGroup>
<Button
android:id="@+id/bt_register"
style="?android:attr/buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/bt_radio_group"
android:layout_marginTop="16dp"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:text="Register"
android:textColor="@android:color/white" />
</RelativeLayout>
package com.irfankhoirul.loginregistermvp.modul.register;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
26
import android.widget.EditText;
import android.widget.RadioButton;
import com.basgeekball.awesomevalidation.AwesomeValidation;
import
com.basgeekball.awesomevalidation.utility.RegexTemplate;
import com.irfankhoirul.loginregistermvp.R;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserRepos
itoryImpl;
import com.irfankhoirul.mvp_core.base.BaseFragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static
com.basgeekball.awesomevalidation.ValidationStyle.TEXT_INPUT_L
AYOUT;
import static
com.google.gson.internal.$Gson$Preconditions.checkNotNull;
@BindView(R.id.et_name)
EditText etName;
@BindView(R.id.et_email)
EditText etEmail;
@BindView(R.id.et_password)
EditText etPassword;
@BindView(R.id.rb_male)
RadioButton rbMale;
@BindView(R.id.rb_female)
RadioButton rbFemale;
public RegisterFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
27
super.onCreateView(inflater, container,
savedInstanceState);
fragmentView =
inflater.inflate(R.layout.fragment_register, container,
false);
unbinder = ButterKnife.bind(this, fragmentView);
return fragmentView;
}
@OnClick(R.id.bt_register)
public void setBtRegisterClick() {
if (validateRegistrationForm()) {
String name = etName.getText().toString();
String email = etEmail.getText().toString();
String password = etPassword.getText().toString();
String gender;
if (rbMale.isChecked()) {
gender = User.GENDER_MALE;
} else {
gender = User.GENDER_FEMALE;
}
mPresenter.performRegister(name, email, gender,
password);
}
}
28
R.string.validation_password_should_not_empty);
return formValidation.validate();
}
@Override
protected void setTitle() {
title = getString(R.string.fragment_title_register);
}
@Override
public void setPresenter(RegisterContract.Presenter
presenter) {
mPresenter = checkNotNull(presenter);
}
@Override
public void setLoadingDialog(boolean isLoading, @Nullable
String message) {
super.setLoadingDialog(isLoading, message);
}
@Override
public void showStatus(int type, String message) {
super.showStatus(type, message);
}
@Override
public void redirectToLogin() {
activity.finish();
}
package com.irfankhoirul.loginregistermvp.modul.register;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserRepos
itory;
import com.irfankhoirul.mvp_core.custom_views.ConstantStatus;
29
import com.irfankhoirul.mvp_core.data.DataResult;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;
@Override
public void start() {
@Override
public void performRegister(String name, String email,
String gender, String password) {
view.setLoadingDialog(true, "Registering");
User user = new User();
user.setName(name);
user.setEmail(email);
user.setGender(gender);
user.setPassword(password);
userRepository.register(user, new
RequestResponseListener() {
@Override
public void onSuccess(DataResult dataResult) {
view.setLoadingDialog(false, null);
if (dataResult.getCode() ==
ConstantStatus.STATUS_SUCCESS) {
view.showStatus(ConstantStatus.STATUS_SUCCESS,
dataResult.getMessage());
view.redirectToLogin();
} else {
view.showStatus(ConstantStatus.STATUS_ERROR,
dataResult.getMessage());
}
30
}
@Override
public void onFailure(Throwable throwable) {
view.setLoadingDialog(false, null);
view.showStatus(ConstantStatus.STATUS_ERROR,
"Registration Failed");
}
});
}
package com.irfankhoirul.loginregistermvp.modul.login;
import android.view.View;
import com.irfankhoirul.loginregistermvp.R;
import
com.irfankhoirul.mvp_core.base.BaseFragmentHolderActivity;
@Override
protected void initializeFragment() {
btBack.setVisibility(View.GONE);
ivIcon.setImageResource(R.mipmap.ic_launcher);
ivIcon.setVisibility(View.VISIBLE);
LoginFragment loginFragment = new LoginFragment();
setCurrentFragment(loginFragment, false);
}
Jadikan LoginActivity sebagai Activity sebagai launcher activity / activity yang pertama kali
dijalankan ketika aplikasi dijalankan. Pastikan terdapat baris berikut di dalam AndroidManifest :
<activity android:name=".modul.login.LoginActivity">
<intent-filter>
31
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
package com.irfankhoirul.loginregistermvp.modul.login;
import com.irfankhoirul.mvp_core.base.BasePresenter;
import com.irfankhoirul.mvp_core.base.BaseView;
<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:padding="16dp"
tools:context="com.irfankhoirul.loginregistermvp.modul.login.Lo
ginFragment">
<android.support.design.widget.TextInputLayout
32
android:id="@+id/til_email"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/til_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/til_email"
app:passwordToggleEnabled="true">
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/bt_login"
style="?android:attr/buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/til_password"
android:layout_marginTop="16dp"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:text="Login"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/tv_no_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
33
android:layout_below="@+id/bt_login"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="Didn't have an account? Register Now" />
<Button
android:id="@+id/bt_register"
style="?android:attr/buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_no_account"
android:layout_marginTop="16dp"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:text="Register"
android:textColor="@android:color/white" />
</RelativeLayout>
package com.irfankhoirul.loginregistermvp.modul.login;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.basgeekball.awesomevalidation.AwesomeValidation;
import com.basgeekball.awesomevalidation.utility.RegexTemplate;
import com.irfankhoirul.loginregistermvp.R;
import
com.irfankhoirul.loginregistermvp.data.source.local.SessionRepo
sitoryImpl;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserReposi
34
toryImpl;
import
com.irfankhoirul.loginregistermvp.modul.profile.ProfileActivity
;
import
com.irfankhoirul.loginregistermvp.modul.register.RegisterActivi
ty;
import com.irfankhoirul.mvp_core.base.BaseFragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static
com.basgeekball.awesomevalidation.ValidationStyle.TEXT_INPUT_LA
YOUT;
import static
com.google.gson.internal.$Gson$Preconditions.checkNotNull;
@BindView(R.id.et_email)
EditText etEmail;
@BindView(R.id.et_password)
EditText etPassword;
public LoginFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container,
savedInstanceState);
fragmentView =
inflater.inflate(R.layout.fragment_login, container, false);
unbinder = ButterKnife.bind(this, fragmentView);
35
return fragmentView;
}
@OnClick(R.id.bt_login)
public void setBtLoginClick() {
if (validateLoginForm()) {
String email = etEmail.getText().toString();
String password = etPassword.getText().toString();
mPresenter.performLogin(email, password);
}
}
@OnClick(R.id.bt_register)
public void setBtRegisterClick() {
Intent intent = new Intent(activity,
RegisterActivity.class);
startActivity(intent);
}
return formValidation.validate();
}
@Override
protected void setTitle() {
title =
getResources().getString(R.string.fragment_title_login);
}
@Override
public void setPresenter(LoginContract.Presenter presenter)
{
mPresenter = checkNotNull(presenter);
}
36
@Override
public void setLoadingDialog(boolean isLoading, @Nullable
String message) {
super.setLoadingDialog(isLoading, message);
}
@Override
public void showStatus(int type, String message) {
super.showStatus(type, message);
}
@Override
public void redirectToProfile() {
Intent intent = new Intent(activity,
ProfileActivity.class);
startActivity(intent);
activity.finish();
}
}
package com.irfankhoirul.loginregistermvp.modul.login;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import
com.irfankhoirul.loginregistermvp.data.source.local.SessionRepo
sitory;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserReposi
tory;
import com.irfankhoirul.mvp_core.custom_views.ConstantStatus;
import com.irfankhoirul.mvp_core.data.DataResult;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;
37
public LoginPresenter(LoginContract.View view,
UserRepository userRepository,
SessionRepository sessionRepository)
{
this.view = view;
this.userRepository = userRepository;
this.sessionRepository = sessionRepository;
this.view.setPresenter(this);
}
@Override
public void start() {
if (isUserLoggedIn()) {
view.redirectToProfile();
}
}
@Override
public void performLogin(String email, String password) {
view.setLoadingDialog(true, "Logging In");
userRepository.login(email, password, new
RequestResponseListener<User>() {
@Override
public void onSuccess(DataResult<User> dataResult)
{
view.setLoadingDialog(false, null);
if (dataResult.getCode() ==
ConstantStatus.STATUS_SUCCESS) {
sessionRepository.initialize(dataResult.getData());
view.showStatus(ConstantStatus.STATUS_SUCCESS,
dataResult.getMessage());
view.redirectToProfile();
} else {
view.showStatus(ConstantStatus.STATUS_ERROR,
dataResult.getMessage());
}
}
@Override
public void onFailure(Throwable throwable) {
view.setLoadingDialog(false, null);
view.showStatus(ConstantStatus.STATUS_ERROR,
"Login failed");
}
38
});
}
package com.irfankhoirul.loginregistermvp.modul.profile;
import android.view.View;
import com.irfankhoirul.loginregistermvp.R;
import
com.irfankhoirul.mvp_core.base.BaseFragmentHolderActivity;
@Override
protected void initializeFragment() {
btBack.setVisibility(View.GONE);
ivIcon.setImageResource(R.mipmap.ic_launcher);
ivIcon.setVisibility(View.VISIBLE);
ProfileFragment profileFragment = new
ProfileFragment();
setCurrentFragment(profileFragment, false);
}
39
Interface ProfileContract menggambarkan interaksi yang dapat dilakukan oleh View
(ProfileFragment) dan Presenter (ProfilePresenter). Buat interface ProfileContract pada package
profile.
package com.irfankhoirul.loginregistermvp.modul.profile;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import com.irfankhoirul.mvp_core.base.BasePresenter;
import com.irfankhoirul.mvp_core.base.BaseView;
void redirectToLogin();
}
<LinearLayout
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="center"
android:orientation="vertical"
tools:context="com.irfankhoirul.loginregistermvp.modul.profile.
ProfileFragment">
<ImageView
android:id="@+id/iv_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_male" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
40
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:textSize="23sp" />
<TextView
android:id="@+id/tv_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt_logout"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_no_account"
android:layout_marginTop="16dp"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="Logout"
android:textColor="@android:color/white" />
</LinearLayout>
package com.irfankhoirul.loginregistermvp.modul.profile;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.irfankhoirul.loginregistermvp.R;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import
41
com.irfankhoirul.loginregistermvp.data.source.local.SessionRepo
sitoryImpl;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserReposi
toryImpl;
import
com.irfankhoirul.loginregistermvp.modul.login.LoginActivity;
import com.irfankhoirul.mvp_core.base.BaseFragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static
com.google.gson.internal.$Gson$Preconditions.checkNotNull;
@BindView(R.id.iv_profile)
ImageView ivProfile;
@BindView(R.id.tv_name)
TextView tvName;
@BindView(R.id.tv_email)
TextView tvEmail;
public ProfileFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container,
savedInstanceState);
fragmentView =
inflater.inflate(R.layout.fragment_profile, container, false);
unbinder = ButterKnife.bind(this, fragmentView);
42
return fragmentView;
}
@OnClick(R.id.bt_logout)
public void setBtLogoutClick() {
mPresenter.performLogout();
}
@Override
protected void setTitle() {
title = getString(R.string.fragment_title_profile);
}
@Override
public void setPresenter(ProfileContract.Presenter
presenter) {
mPresenter = checkNotNull(presenter);
}
@Override
public void setLoadingDialog(boolean isLoading, @Nullable
String message) {
super.setLoadingDialog(isLoading, message);
}
@Override
public void showStatus(int type, String message) {
super.showStatus(type, message);
}
@Override
public void showProfile(User user) {
if (user.getGender().equals(User.GENDER_MALE)) {
ivProfile.setImageResource(R.drawable.ic_male);
} else {
ivProfile.setImageResource(R.drawable.ic_female);
}
tvName.setText(user.getName());
tvEmail.setText(user.getEmail());
}
@Override
public void redirectToLogin() {
Intent intent = new Intent(activity,
LoginActivity.class);
startActivity(intent);
43
}
}
package com.irfankhoirul.loginregistermvp.modul.profile;
import com.irfankhoirul.loginregistermvp.data.pojo.User;
import
com.irfankhoirul.loginregistermvp.data.source.local.SessionRepo
sitory;
import
com.irfankhoirul.loginregistermvp.data.source.remote.UserReposi
tory;
import com.irfankhoirul.mvp_core.custom_views.ConstantStatus;
import com.irfankhoirul.mvp_core.data.DataResult;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;
@Override
public void start() {
user = getUserData();
view.showProfile(user);
}
@Override
public void performLogout() {
44
view.setLoadingDialog(true, "Logging out");
userRepository.logout(user.getAuthToken(), new
RequestResponseListener() {
@Override
public void onSuccess(DataResult dataResult) {
view.setLoadingDialog(false, null);
if (dataResult.getCode() ==
ConstantStatus.STATUS_SUCCESS) {
sessionRepository.destroy();
view.showStatus(ConstantStatus.STATUS_SUCCESS,
dataResult.getMessage());
view.redirectToLogin();
} else {
view.showStatus(ConstantStatus.STATUS_ERROR,
dataResult.getMessage());
}
}
@Override
public void onFailure(Throwable throwable) {
view.setLoadingDialog(false, null);
view.showStatus(ConstantStatus.STATUS_ERROR,
"Logout Failed");
}
});
}
<string name="fragment_title_login">Login</string>
<string name="fragment_title_profile">Profile</string>
<string name="fragment_title_register">Register</string>
<string name="validation_email_must_be_valid">Email must be
valid!</string>
<string name="validation_email_should_not_empty">Email should
not empty!</string>
45
<string name="validation_name_should_not_empty">Name should
not empty!</string>
<string name="validation_password_should_not_empty">Password
should not empty!</string>
2. Drawable
Tambahkan (Copy - Paste) 2 file berikut ke dalam direktori res/drawable.
a. ic_male
https://raw.githubusercontent.com/irfankhoirul/LoginRegisterMVP/master/app/src/ma
in/res/drawable/ic_male.xml
b. ic_female
https://raw.githubusercontent.com/irfankhoirul/LoginRegisterMVP/master/app/src/ma
in/res/drawable/ic_female.xml
F. Hasil
46