Anda di halaman 1dari 46

Aplikasi Android untuk Register – Login dengan PHP, MYSQL, dan

Model View Presenter


Oleh : Irfan Khoirul Mulishin / 2110155022

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.

Hasil yang sudah jadi dapat dilihat di :

- 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.

Berikut adalah gambaran perbandingan ketiga arsitektur tersebut.

1
Penerapan MVP pada Android adalah sebagai berikut :

 Model  Custom Class


Berfungsi untuk melakukan data access, baik itu pada data lokal maupun remote.
 View  Activity / Fragment / DialogFragment / View (android.view)
Berfungsi untuk menerima input dari user dan menampilkan feedback dari input tersebut (UI
Logic).
 Presenter  Custom Class
Presenter melakukan pengaksesan ke model dan mengupdate view berdasarkan interaksi yang
dilakukan oleh user. Singkatnya, Presenter berfungsi untuk menjembatani View dan Model.

2
DATABASE

Hal yang harus anda siapkan sebelumnya :

- MySQL server
- Aplikasi pengelola MySQL (MySQL Workbench / PHPMyAdmin/ etc)

Mula-mula, siapkan database untuk menyimpan data user.

Steps :

1. Buat database dan tabel users menggunakan PHPMyAdmin / lainnya

create database android_api;

use android_api;

create table users(


id int(11) primary key AUTO_INCREMENT,
name varchar(50) not null,
email varchar(100) not null unique,
gender char(1),
encrypted_password varchar(64) not null,
salt varchar(32) not null,
is_login boolean,
created_at datetime,
updated_at datetime,
auth_token varchar(32)
);

3
WEB SERVICE

Hal yang diperlukan :

- 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

2. Jalankan Laravel setelah installasi berhasil

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;

class User extends Model


{

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";

protected $table = 'users';

public function register($userData)


{
try {
$user = DB::table($this->table)->where('email',
$userData['email'])->first();
if ($user != NULL) {
return array(self::CODE_ERROR, "Alamat Email
yang Anda masukkan sudah digunakan untuk registrasi", NULL);
}

$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 ($user != NULL && $user->encrypted_password ==


hash('sha256', $user->salt . hash('md5', $userData['password']
. $user->salt))) {
$authToken = str_random(32);
DB::table($this->table)
->where('email', $userData['email'])
->update(
['is_login' => TRUE,
'updated_at' => date("Y-m-d
H:i:s"),
'auth_token' => $authToken
]);
$user->auth_token = $authToken;
return array(self::CODE_SUCCESS,
self::RESULT_LOGIN_SUCCESS, NULL, $user);
} else {
return array(self::CODE_ERROR,
self::RESULT_WRONG_PASSWORD, NULL, NULL);
}
} catch (QueryException $ex) {
return array(self::CODE_ERROR,
self::RESULT_LOGIN_FAILED, $ex->getMessage(), NULL);
}
}

public function logout($token)


{
try {
if ($token != NULL) {
$result = DB::table($this->table)
->where('auth_token', $token)
->update(
6
['is_login' => FALSE,
'updated_at' => date("Y-m-d
H:i:s"),
'auth_token' => 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;

class BaseController extends Controller


{

const CODE_SUCCESS = 1;
const CODE_ERROR = 0;

protected function returnJson($code, $message,


$technicalMessage, $data)
{
$result = array();
$result['code'] = $code;
$result['message'] = $message;
if (config('app.debug')) {
$result['debugMessage'] = $technicalMessage;

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
{

public function register(Request $request)


{
$validator = Validator::make($request->all(), [

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 ($userData['gender'] != "M" && $userData['gender']


!= "F") {
$this->returnJsonErrorDataNotValid("Gender must be
'M' of 'F'");
}

$user = new User();


list($status, $message, $technicalMessage) = $user-
>register($userData);
$this->returnJson($status, $message, $technicalMessage,
null);
}

public function login(Request $request)


{
$validator = Validator::make($request->all(), [
'email' => 'required|email|max:100',
'password' => 'required'
]);

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);
}

public function logout(Request $request)


{
$validator = Validator::make($request->all(), [
'token' => 'required'
]);

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

Hal yang diperlukan :

- Android Studio (termasuk JDK)

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 :

A. Membuat Project Baru


1. Buat Project Baru pada Android Studio

12
2. Tentukan SDK Minimum, pada contoh ini adalah Jelly Bean 4.1

3. Pilih Add No Activity

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'

MVPCore digunakan untuk mengimplementasikan MVP pattern pada aplikasi yang


dibuat. Gson digunakan untuk melakukan serialize / deserialize (konversi) JSON ke Java
Object. Retrofit digunakan untuk mengakses web service. Butterknife digunakan untuk
binding resource (view) ke Java Object.

b. build.gradle (Project level)

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.

Sebelum memulai menulis kode, siapkan package seperti berikut :

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;

public class User extends BasePojo {

public static final String GENDER_MALE = "M";


public static final String GENDER_FEMALE = "F";

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;

private String password;

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;
}

public String getAuthToken() {


return authToken;
}

public void setAuthToken(String authToken) {


this.authToken = authToken;
}

public String getPassword() {


return password;
}

16
public void setPassword(String password) {
this.password = password;
}

public String getGender() {


return gender;
}

public void setGender(String gender) {


this.gender = gender;
}
}

2. Membuat interface UserEndPoints


Kelas UserEndPoints adalah interface yang digunakan oleh library Retrofit untuk
mendeklarasikan EndPoint yang akan diakses. Buat interface UserEndPoint pada package
remote.

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;

public interface UserEndPoints {


String REGISTER_END_POINT = "register";
String LOGIN_END_POINT = "login";
String LOGOUT_END_POINT = "logout";

@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);

3. Membuat interface UserRepository


Interface UserRepository berisi Use Case yang dihandle oleh UserRepository, terdiri dari register,
login, dan logout. Buat interface UserRepository pada package remote.

package com.irfankhoirul.loginregistermvp.data.source.remote;

import com.irfankhoirul.loginregistermvp.data.pojo.User;
import com.irfankhoirul.mvp_core.data.RequestResponseListener;

public interface UserRepository {


void register(User user, RequestResponseListener
listener);

void login(String email, String password,


RequestResponseListener<User> listener);

void logout(String token, RequestResponseListener


listener);
}

4. Membuat kelas UserRepositoryImpl


Kelas UserRepositoryImpl berisi implementasi dari setiap UseCase yang terdapat pada interface
UserRepository. Buat interface UserRepositoryImpl pada package remote.

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;

public class UserRepositoryImpl extends


BaseRemoteRepository<UserEndPoints> implements UserRepository
{

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);
}
}

5. Membuat interface SessionRepository


Interface SessionRepository berisi UseCase yang berkaitan dengan session dan dihandle oleh
SessionRepository. Buat interface SessionRepository pada package local.

package com.irfankhoirul.loginregistermvp.data.source.local;

import com.irfankhoirul.loginregistermvp.data.pojo.User;

public interface SessionRepository {


User initialize(User sessionData);

User getSessionData();

void setSessionData(User sessionData);

void destroy();
}

6. Membuat kelas SessionRepositoryImpl


Kelas SessionRepositoryImpl berisi implementasi dari setiap UseCase yang terdapat pada
interface SessionRepository. Buat interface SessionRepositoryImpl pada package remote.

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;

public class SessionRepositoryImpl implements


SessionRepository {
private static String SESSION_DATA_KEY = "SessionData";
private static String SHARED_PREFERENCE_NAME =
"SessionSharedPreferences";

private SharedPreferences sharedPref;

public SessionRepositoryImpl(Context context) {


sharedPref =
context.getSharedPreferences(SHARED_PREFERENCE_NAME,

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();
}

C. Membuat Modul Register


1. Membuat kelas RegisterActivity

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;

public class RegisterActivity extends


BaseFragmentHolderActivity {

@Override
protected void initializeFragment() {
RegisterFragment registerFragment = new
RegisterFragment();
setCurrentFragment(registerFragment, false);
}

23
}

2. Membuat interface RegisterContract


Interface RegisterContract berisi 2 innner interface, View dan Presenter. Interface View berisi
UseCase yang dapat dilakukan oleh View dari Presenter, begitu sebaliknya interface Presenter.
Interface RegisterContract menggambarkan interaksi yang dapat dilakukan oleh View
(RegisterFragment) dan Presenter (RegisterPresenter). Buat interface RegisterContract pada
package register.

package com.irfankhoirul.loginregistermvp.modul.register;

import com.irfankhoirul.mvp_core.base.BasePresenter;
import com.irfankhoirul.mvp_core.base.BaseView;

public interface RegisterContract {


interface View extends BaseView<Presenter> {
void redirectToLogin();
}

interface Presenter extends BasePresenter {


void performRegister(String name, String email, String
gender, String password);
}
}

3. Membuat layout fragment_register


Buat layout fragment_register pada directory res/layout.

<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>

4. Membuat kelas RegisterFragment


Kelas RegisterFragment bertindak sebagai View dari module Register, oleh karena itu kelas ini
mengimplementasikan interface View yang terdapat dalam interface RegisterContract. Buat
kelas RegisterFragment pada package register.

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;

public class RegisterFragment extends


BaseFragment<RegisterActivity, RegisterContract.Presenter>
implements RegisterContract.View {

@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);

mPresenter = new RegisterPresenter(this, new


UserRepositoryImpl());
mPresenter.start();

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);
}
}

private boolean validateRegistrationForm() {


AwesomeValidation formValidation = new
AwesomeValidation(TEXT_INPUT_LAYOUT);
formValidation.addValidation(activity, R.id.til_email,
Patterns.EMAIL_ADDRESS,
R.string.validation_email_must_be_valid);
formValidation.addValidation(activity, R.id.til_email,
RegexTemplate.NOT_EMPTY,
R.string.validation_email_should_not_empty);
formValidation.addValidation(activity, R.id.til_name,
RegexTemplate.NOT_EMPTY,
R.string.validation_name_should_not_empty);
formValidation.addValidation(activity,
R.id.til_password, RegexTemplate.NOT_EMPTY,

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();
}

5. Membuat kelas RegisterPresenter


Kelas RegisterPresenter bertindak sebagai Presenter dari module Register, oleh karena itu kelas
ini mengimplementasikan interface Presenter yang terdapat di dalam RegisterContract. Buat
kelas RegisterPresenter di dalam package register.

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;

public class RegisterPresenter implements


RegisterContract.Presenter {

private final RegisterContract.View view;


private final UserRepository userRepository;

public RegisterPresenter(RegisterContract.View view,


UserRepository userRepository) {
this.view = view;
this.userRepository = userRepository;
this.view.setPresenter(this);
}

@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");
}
});
}

D. Membuat Modul Login


1. Membuat kelas LoginActivity
Buat kelas LoginActivity pada package login melalui menu New->Activity->EmptyActivity.
Activity ini tidak memerlukan LayoutFile, jadi pastikan tidak ada centang pada Generate Layout
File.

package com.irfankhoirul.loginregistermvp.modul.login;

import android.view.View;

import com.irfankhoirul.loginregistermvp.R;
import
com.irfankhoirul.mvp_core.base.BaseFragmentHolderActivity;

public class LoginActivity extends 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>

2. Membuat interface LoginContract


Interface LoginContract berisi 2 innner interface, View dan Presenter. Interface View berisi
UseCase yang dapat dilakukan oleh View dari Presenter, begitu sebaliknya interface Presenter.
Interface LoginContract menggambarkan interaksi yang dapat dilakukan oleh View
(LoginFragment) dan Presenter (LoginPresenter). Buat interface LoginContract pada package
login.

package com.irfankhoirul.loginregistermvp.modul.login;

import com.irfankhoirul.mvp_core.base.BasePresenter;
import com.irfankhoirul.mvp_core.base.BaseView;

public interface LoginContract {


interface View extends BaseView<Presenter> {
void redirectToProfile();
}

interface Presenter extends BasePresenter {


void performLogin(String email, String password);
}
}

3. Membuat layout fragment_login


Buat layout fragment_login pada directory res/layout.

<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>

4. Membuat kelas LoginFragment


Kelas LoginFragment bertindak sebagai View dari module Login, oleh karena itu kelas ini
mengimplementasikan interface View yang terdapat dalam interface LoginContract. Buat kelas
LoginFragment pada package login.

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;

public class LoginFragment extends BaseFragment<LoginActivity,


LoginContract.Presenter> implements LoginContract.View {

@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);

mPresenter = new LoginPresenter(this, new


UserRepositoryImpl(),
new SessionRepositoryImpl(getActivity()));
mPresenter.start();

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);
}

private boolean validateLoginForm() {


AwesomeValidation formValidation = new
AwesomeValidation(TEXT_INPUT_LAYOUT);
formValidation.addValidation(activity, R.id.til_email,
Patterns.EMAIL_ADDRESS,
R.string.validation_email_must_be_valid);
formValidation.addValidation(activity, R.id.til_email,
RegexTemplate.NOT_EMPTY,
R.string.validation_email_should_not_empty);
formValidation.addValidation(activity,
R.id.til_password, RegexTemplate.NOT_EMPTY,
R.string.validation_password_should_not_empty);

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();
}
}

5. Membuat kelas LoginPresenter


Kelas LoginPresenter bertindak sebagai Presenter dari module Login, oleh karena itu kelas ini
mengimplementasikan interface Presenter yang terdapat di dalam LoginContract. Buat kelas
LoginPresenter di dalam package login.

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;

public class LoginPresenter implements LoginContract.Presenter


{

private final LoginContract.View view;


private final UserRepository userRepository;
private final SessionRepository sessionRepository;

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
});
}

private boolean isUserLoggedIn() {


if (sessionRepository.getSessionData() != null) {
return true;
}
return false;
}

6. Membuat Modul Profile


1. Membuat kelas ProfileActivity
Buat kelas ProfileActivity pada package profile melalui menu New->Activity->EmptyActivity.
Activity ini tidak memerlukan LayoutFile, jadi pastikan tidak ada centang pada Generate Layout
File.

package com.irfankhoirul.loginregistermvp.modul.profile;

import android.view.View;

import com.irfankhoirul.loginregistermvp.R;
import
com.irfankhoirul.mvp_core.base.BaseFragmentHolderActivity;

public class ProfileActivity extends 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);
}

2. Membuat interface ProfileContract


Interface ProfileContract berisi 2 innner interface, View dan Presenter. Interface View berisi
UseCase yang dapat dilakukan oleh View dari Presenter, begitu sebaliknya interface Presenter.

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;

public interface ProfileContract {


interface View extends BaseView<Presenter> {
void showProfile(User user);

void redirectToLogin();
}

interface Presenter extends BasePresenter {


void performLogout();
}
}

3. Membuat layout fragment_profile


Buat layout fragment_profile pada directory res/layout.

<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>

4. Membuat kelas ProfileFragment


Kelas ProfileFragment bertindak sebagai View dari module Profile, oleh karena itu kelas ini
mengimplementasikan interface View yang terdapat dalam interface ProfileContract. Buat kelas
ProfileFragment pada package profile.

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;

public class ProfileFragment extends


BaseFragment<ProfileActivity, ProfileContract.Presenter>
implements ProfileContract.View {

@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);

mPresenter = new ProfilePresenter(this, new


SessionRepositoryImpl(activity),
new UserRepositoryImpl());
mPresenter.start();

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
}
}

5. Membuat kelas ProfilePresenter


Kelas ProfilePresenter bertindak sebagai Presenter dari module Profile, oleh karena itu kelas ini
mengimplementasikan interface Presenter yang terdapat di dalam ProfileContract. Buat kelas
ProfilePresenter di dalam package profile.

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;

public class ProfilePresenter implements


ProfileContract.Presenter {

private final ProfileContract.View view;


private final SessionRepository sessionRepository;
private final UserRepository userRepository;
private User user;

public ProfilePresenter(ProfileContract.View view,


SessionRepository sessionRepository,
UserRepository userRepository) {
this.view = view;
this.userRepository = userRepository;
this.sessionRepository = sessionRepository;
this.view.setPresenter(this);
}

@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");
}
});
}

private User getUserData() {


return sessionRepository.getSessionData();
}
}

E. Menambahkan File Pendukung


1. Strings.xml
Pastikan terdapat baris berikut pada file strings.xml yang terdapat pada direktory res/values.

<string name="app_name">Login Register MVP</string>

<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

Anda mungkin juga menyukai