Welcome to Retrofit Android Tutorial guys. Today we will learn to build a simple application in android using Retrofit Android Library and SLIM PHP Framework.
Today, we will be covering a lot of things. We will see user registration, user login, session management and many more things. So keep reading carefully as this is going to be a long tutorial.
Table of Contents
Retrofit Android Tutorial – Complete Video Series
- If you want a complete explanation about building API with PHP and SLIM and using them in Android with Retrofit then you should watch this Play List. (Don’t forget to share with your friends).Â
The App Overview
- So lets first understand what we are going to build. The below diagram will explain you in detail.
- As you can see in the above diagram we are going to build a very simple application. Where the registered user can send messages to each other.
- Now the next step is designing the database. I am going to use MySQL for this.
Database Design
- This is our database model.
- It is very simple and to create the above database just go to phpmyadmin create a database and run the following SQL code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
-- Created by Simplified Coding -- tables -- Table: messages CREATE TABLE messages ( id int NOT NULL AUTO_INCREMENT, from_users_id int NOT NULL, to_users_id int NOT NULL, title varchar(100) NOT NULL, message varchar(1000) NOT NULL, sentat timestamp NOT NULL, CONSTRAINT messages_pk PRIMARY KEY (id) ); -- Table: users CREATE TABLE users ( id int NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, email varchar(100) NOT NULL, password text NOT NULL, gender varchar(6) NOT NULL, CONSTRAINT users_pk PRIMARY KEY (id) ); -- foreign keys -- Reference: messages_users_from (table: messages) ALTER TABLE messages ADD CONSTRAINT messages_users_from FOREIGN KEY messages_users_from (to_users_id) REFERENCES users (id); -- Reference: messages_users_to (table: messages) ALTER TABLE messages ADD CONSTRAINT messages_users_to FOREIGN KEY messages_users_to (from_users_id) REFERENCES users (id); -- End of file. |
- Now lets create the REST API.
Building RESTful API using SLIM
Creating Project
- First go to xampp/htdocs and open terminal here. (if you are using windows go to c:/xampp/htdocs in command prompt).
- Now run the following command to create a new project. (Make sure you have composer installed).
1 2 3 |
composer create-project slim/slim-skeleton RetrofitExample |
- In the above line RetrofitExample is the project name. You can change it with whatever you want.
- After executing the above command your php project will be created. Now you can use any Text Editor to work on this project. I am using PHPStorm.
- You will see the following directory structure in the project you just created.
- Open index.php and delete everything that is inside.
- Now inside RetrofitExample (Your project directory) create one more directory named includes.
Defining Constants
- Inside includes create a new php file named Constants.php it will store all the required constants. Make sure to change the database name according to your database in the below script.
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php define('DB_USERNAME', 'root'); define('DB_PASSWORD', ''); define('DB_HOST', 'localhost'); define('DB_NAME', 'retrofit'); define('USER_CREATED', 101); define('USER_EXIST', 102); define('USER_CREATION_FAILED', 103); |
Connecting with Database
- Now in the same directory create one more php file named DbConnect.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php class DbConnect { //Variable to store database link private $con; //Class constructor function __construct() { } //This method will connect to the database function connect() { //Including the constants.php file to get the database constants include_once dirname(__FILE__) . '/Constants.php'; //connecting to mysql database $this->con = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME); //Checking if any error occured while connecting if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); return null; } //finally returning the connection link return $this->con; } } |
- The above script will connect with the MySQL database.
Performing Database Operation
- Now create one more php script named DbOperation.php that will perform all the required database operations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
<?php class DbOperation { private $con; function __construct() { require_once dirname(__FILE__) . '/DbConnect.php'; $db = new DbConnect(); $this->con = $db->connect(); } //Method to create a new user function registerUser($name, $email, $pass, $gender) { if (!$this->isUserExist($email)) { $password = md5($pass); $stmt = $this->con->prepare("INSERT INTO users (name, email, password, gender) VALUES (?, ?, ?, ?)"); $stmt->bind_param("ssss", $name, $email, $password, $gender); if ($stmt->execute()) return USER_CREATED; return USER_CREATION_FAILED; } return USER_EXIST; } //Method for user login function userLogin($email, $pass) { $password = md5($pass); $stmt = $this->con->prepare("SELECT id FROM users WHERE email = ? AND password = ?"); $stmt->bind_param("ss", $email, $password); $stmt->execute(); $stmt->store_result(); return $stmt->num_rows > 0; } //Method to send a message to another user function sendMessage($from, $to, $title, $message) { $stmt = $this->con->prepare("INSERT INTO messages (from_users_id, to_users_id, title, message) VALUES (?, ?, ?, ?);"); $stmt->bind_param("iiss", $from, $to, $title, $message); if ($stmt->execute()) return true; return false; } //Method to update profile of user function updateProfile($id, $name, $email, $pass, $gender) { $password = md5($pass); $stmt = $this->con->prepare("UPDATE users SET name = ?, email = ?, password = ?, gender = ? WHERE id = ?"); $stmt->bind_param("ssssi", $name, $email, $password, $gender, $id); if ($stmt->execute()) return true; return false; } //Method to get messages of a particular user function getMessages($userid) { $stmt = $this->con->prepare("SELECT messages.id, (SELECT users.name FROM users WHERE users.id = messages.from_users_id) as `from`, (SELECT users.name FROM users WHERE users.id = messages.to_users_id) as `to`, messages.title, messages.message, messages.sentat FROM messages WHERE messages.to_users_id = ?;"); $stmt->bind_param("i", $userid); $stmt->execute(); $stmt->bind_result($id, $from, $to, $title, $message, $sent); $messages = array(); while ($stmt->fetch()) { $temp = array(); $temp['id'] = $id; $temp['from'] = $from; $temp['to'] = $to; $temp['title'] = $title; $temp['message'] = $message; $temp['sent'] = $sent; array_push($messages, $temp); } return $messages; } //Method to get user by email function getUserByEmail($email) { $stmt = $this->con->prepare("SELECT id, name, email, gender FROM users WHERE email = ?"); $stmt->bind_param("s", $email); $stmt->execute(); $stmt->bind_result($id, $name, $email, $gender); $stmt->fetch(); $user = array(); $user['id'] = $id; $user['name'] = $name; $user['email'] = $email; $user['gender'] = $gender; return $user; } //Method to get all users function getAllUsers(){ $stmt = $this->con->prepare("SELECT id, name, email, gender FROM users"); $stmt->execute(); $stmt->bind_result($id, $name, $email, $gender); $users = array(); while($stmt->fetch()){ $temp = array(); $temp['id'] = $id; $temp['name'] = $name; $temp['email'] = $email; $temp['gender'] = $gender; array_push($users, $temp); } return $users; } //Method to check if email already exist function isUserExist($email) { $stmt = $this->con->prepare("SELECT id FROM users WHERE email = ?"); $stmt->bind_param("s", $email); $stmt->execute(); $stmt->store_result(); return $stmt->num_rows > 0; } } |
Handling API Calls
- Now come inside index.php that is inside the public folder and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
<?php use \Psr\Http\Message\ServerRequestInterface as Request; use \Psr\Http\Message\ResponseInterface as Response; require '../vendor/autoload.php'; require_once '../includes/DbOperation.php'; //Creating a new app with the config to show errors $app = new \Slim\App([ 'settings' => [ 'displayErrorDetails' => true ] ]); //registering a new user $app->post('/register', function (Request $request, Response $response) { if (isTheseParametersAvailable(array('name', 'email', 'password', 'gender'))) { $requestData = $request->getParsedBody(); $name = $requestData['name']; $email = $requestData['email']; $password = $requestData['password']; $gender = $requestData['gender']; $db = new DbOperation(); $responseData = array(); $result = $db->registerUser($name, $email, $password, $gender); if ($result == USER_CREATED) { $responseData['error'] = false; $responseData['message'] = 'Registered successfully'; $responseData['user'] = $db->getUserByEmail($email); } elseif ($result == USER_CREATION_FAILED) { $responseData['error'] = true; $responseData['message'] = 'Some error occurred'; } elseif ($result == USER_EXIST) { $responseData['error'] = true; $responseData['message'] = 'This email already exist, please login'; } $response->getBody()->write(json_encode($responseData)); } }); //user login route $app->post('/login', function (Request $request, Response $response) { if (isTheseParametersAvailable(array('email', 'password'))) { $requestData = $request->getParsedBody(); $email = $requestData['email']; $password = $requestData['password']; $db = new DbOperation(); $responseData = array(); if ($db->userLogin($email, $password)) { $responseData['error'] = false; $responseData['user'] = $db->getUserByEmail($email); } else { $responseData['error'] = true; $responseData['message'] = 'Invalid email or password'; } $response->getBody()->write(json_encode($responseData)); } }); //getting all users $app->get('/users', function (Request $request, Response $response) { $db = new DbOperation(); $users = $db->getAllUsers(); $response->getBody()->write(json_encode(array("users" => $users))); }); //getting messages for a user $app->get('/messages/{id}', function (Request $request, Response $response) { $userid = $request->getAttribute('id'); $db = new DbOperation(); $messages = $db->getMessages($userid); $response->getBody()->write(json_encode(array("messages" => $messages))); }); //updating a user $app->post('/update/{id}', function (Request $request, Response $response) { if (isTheseParametersAvailable(array('name', 'email', 'password', 'gender'))) { $id = $request->getAttribute('id'); $requestData = $request->getParsedBody(); $name = $requestData['name']; $email = $requestData['email']; $password = $requestData['password']; $gender = $requestData['gender']; $db = new DbOperation(); $responseData = array(); if ($db->updateProfile($id, $name, $email, $password, $gender)) { $responseData['error'] = false; $responseData['message'] = 'Updated successfully'; $responseData['user'] = $db->getUserByEmail($email); } else { $responseData['error'] = true; $responseData['message'] = 'Not updated'; } $response->getBody()->write(json_encode($responseData)); } }); //sending message to user $app->post('/sendmessage', function (Request $request, Response $response) { if (isTheseParametersAvailable(array('from', 'to', 'title', 'message'))) { $requestData = $request->getParsedBody(); $from = $requestData['from']; $to = $requestData['to']; $title = $requestData['title']; $message = $requestData['message']; $db = new DbOperation(); $responseData = array(); if ($db->sendMessage($from, $to, $title, $message)) { $responseData['error'] = false; $responseData['message'] = 'Message sent successfully'; } else { $responseData['error'] = true; $responseData['message'] = 'Could not send message'; } $response->getBody()->write(json_encode($responseData)); } }); //function to check parameters function isTheseParametersAvailable($required_fields) { $error = false; $error_fields = ""; $request_params = $_REQUEST; foreach ($required_fields as $field) { if (!isset($request_params[$field]) || strlen(trim($request_params[$field])) <= 0) { $error = true; $error_fields .= $field . ', '; } } if ($error) { $response = array(); $response["error"] = true; $response["message"] = 'Required field(s) ' . substr($error_fields, 0, -2) . ' is missing or empty'; echo json_encode($response); return false; } return true; } $app->run(); |
- The API Building is finished. I haven’t explain this part too much as we already did it in one of the previous tutorials about PHP Restful API using SLIM.
Testing the APIs
- Now lets test the APIs. For testing I am using Postman you can use any REST Client. So the URLs we have are.
URL | Method | Parameters |
http://localhost/RetrofitExample/public/register | POST | name, email, password, gender |
http://localhost/RetrofitExample/public/login | POST | email, password |
http://localhost/RetrofitExample/public/messages/{id} | GET | |
http://localhost/RetrofitExample/public/update/{id} | POST | name, email, password, gender |
http://localhost/RetrofitExample/public/sendmessage | POST | from, to, title, message |
http://localhost/RetrofitExample/public/users | GET |
http://localhost/RetrofitExample/public/register
- The same way you can test all the URLs and then we can start developing the Android Application.
Building Android Application using Retrofit
Creating Project
- Create a new Android Studio Project. I created a project named RetrofitExample.
- Once your project is loaded go to app level build.gradle file and add the following dependencies.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.2.1' testCompile 'junit:junit:4.12' //add the following dependencies compile 'com.squareup.retrofit2:retrofit:2.2.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' } |
- Now sync your project with gradle and Retrofit and Gson is added.
Designing Screens
First Screen
- So our first screen is having two buttons for Sign In or Sign Up. So inside activity_main.xml paste the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="net.simplifiedcoding.retrofitexample.activities.MainActivity"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="horizontal"> <Button android:id="@+id/buttonSignIn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="Sign In" /> <Button android:id="@+id/buttonSignUp" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="Sign Up" /> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/imageView" android:layout_alignParentStart="true" android:text="Simplified Coding" android:textAlignment="center" android:textAppearance="@style/Base.TextAppearance.AppCompat.Large" /> </RelativeLayout> |
- The above code will generate the following layout.
- This is screen is very simple, having only two buttons that will take us to the respective screens.
Sign In Screen
- Now create a new Empty Activity named SignInActivity and inside the layout file of this activity write the following xml code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_sign_in" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="net.simplifiedcoding.retrofitexample.activities.SignInActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical"> <EditText android:id="@+id/editTextEmail" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter email " android:inputType="textEmailAddress" /> <EditText android:id="@+id/editTextPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter password " android:inputType="textPassword" /> <Button android:id="@+id/buttonSignIn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Sign In" /> </LinearLayout> </RelativeLayout> |
- This screen will look like as below.
Sign Up Screen
- Now again create a new Empty Activity for Sign Up Screen and write the following xml code in the layout file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_sign_up" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="net.simplifiedcoding.retrofitexample.activities.SignUpActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical"> <EditText android:id="@+id/editTextName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter name " android:inputType="text" /> <EditText android:id="@+id/editTextEmail" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter email " android:inputType="textEmailAddress" /> <EditText android:id="@+id/editTextPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter password " android:inputType="textPassword" /> <RadioGroup android:id="@+id/radioGender" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/radioMale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Male" /> <RadioButton android:id="@+id/radioFemale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Female" /> </RadioGroup> <Button android:id="@+id/buttonSignIn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Sign Up" /> </LinearLayout> </RelativeLayout> |
- This screen will look like as below.
Home Screen
- User will come to this screen after Signing In. This will be a Navigation Drawer Activity. So create a Navigation Drawer Activity for the Home Screen.
- Creating a Navigation drawer activity will create a number of layout files inside the layout directory. First come to nav_header_home.xml and modify it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="@dimen/nav_header_height" android:background="@drawable/side_nav_bar" android:gravity="bottom" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <TextView android:id="@+id/textViewName" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/nav_header_vertical_spacing" android:text="Android Studio" android:textAppearance="@style/TextAppearance.AppCompat.Large" /> </LinearLayout> |
- Now open app_bar_home.xml and remove the floating action button.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="net.simplifiedcoding.retrofitexample.activities.HomeActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_home" /> </android.support.design.widget.CoordinatorLayout> |
- Now go to menu folder and you will see activity_home_drawer.xml change it to the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/nav_home" android:title="Home" /> <item android:id="@+id/nav_profile" android:title="Profile" /> <item android:id="@+id/nav_messages" android:title="Messages" /> <item android:id="@+id/nav_logout" android:title="Logout" /> </group> </menu> |
- Now we will display all the remaining screen inside this home screen using fragments. So lets quickly design the layout for all other screens.
Designing Home Fragment
- As we decided in the app that in the home screen we will display all users. For this we will use RecyclerView and CardView. So first you need to add RecyclerView and CardView in your project. For this go to project structure->app->dependencies and add library dependency with CardView and RecyclerView. For details you can check this RecyclerView tutorial.
- Create a new layout file named fragment_home.xml and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?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"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerViewUsers" android:layout_width="match_parent" android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView> </LinearLayout> |
- As you can see we have a RecyclerView in fragment_home, so we need to create one more layout for the RecyclerView. So create a new layout file named list_users.xml and we will display all the users of the application in the list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?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="wrap_content" android:orientation="vertical"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp"> <TextView android:id="@+id/textViewName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="2" android:text="Belal Khan" android:textAppearance="@style/Base.TextAppearance.AppCompat.Large" /> <ImageButton android:id="@+id/imageButtonMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/icon_message" /> </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout> |
- Now in this screen we will also have an alert dialog to send messages to the users. We will also design this dialog.
Designing Alert Message Dialog
- Create a file named dialog_send_message.xml and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:padding="@dimen/activity_horizontal_margin" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/editTextTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter title" /> <EditText android:id="@+id/editTextMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter Message" android:lines="7" /> </LinearLayout> |
- Now lets design the profile fragment screen.
Designing Profile Fragment
- Again create a new layout file named fragment_profile.xml and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical"> <EditText android:id="@+id/editTextName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter name " android:inputType="text" /> <EditText android:id="@+id/editTextEmail" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter email " android:inputType="textEmailAddress" /> <EditText android:id="@+id/editTextPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter password " android:inputType="textPassword" /> <RadioGroup android:id="@+id/radioGender" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/radioMale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Male" /> <RadioButton android:id="@+id/radioFemale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Female" /> </RadioGroup> <Button android:id="@+id/buttonSignIn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Update" /> </LinearLayout> </RelativeLayout> |
- It will look like this.
- Now the last fragment is Messages fragment.
Designing Messages Fragment
- Again create a new file named fragment_messages.xml and write the following code. Here we are again using RecyclerView to display the messages received by the user in a list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerViewMessages" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView> </LinearLayout> |
- Again we will define a list layout for the recycler view. So create a file named list_messages.xml and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?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="wrap_content" android:orientation="vertical"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp"> <TextView android:id="@+id/textViewName" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Belal Khan" /> <TextView android:id="@+id/textViewTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Greetings!" android:textAppearance="@style/Base.TextAppearance.AppCompat.Large" /> <TextView android:id="@+id/textViewMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Lorem ipsum dolor sit camet lorem ipsum dolor sit camet lorem ipsum" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" /> <TextView android:id="@+id/textViewTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="30/04/1993 10:25am" /> </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout> |
- So we have designed all the screen needed in the application. Now lets start coding the application.
Coding Screens
- First create 4 packages inside your main package to make everything organized. I have created activities, api, fragments, model.Â
- As you can see I have moved all the activities inside activities package. This is only to keep the things organized. Now come to MainActivity.java.
Defining Base URL
- Inside api package create a class named APIUrl.java and inside this class we will define the base url of our API. Remember you are not allowed to use localhost here, you need to find the ip of your system if you are using localhost (I am using xampp). For windows use ipconfig and for Mac or Linux use ifconfig to find the ip.
1 2 3 4 5 6 7 8 9 10 11 |
package net.simplifiedcoding.retrofitexample.api; /** * Created by Belal on 14/04/17. */ public class APIUrl { public static final String BASE_URL = "http://192.168.1.102/RetrofitExample/public/"; } |
Adding Internet Permission
- Go to AndroidManifest.xml and add internet permission.
1 2 3 |
<uses-permission android:name="android.permission.INTERNET" /> |
Coding MainActivity
- MainActivity is very simple we only need to navigate to the respective screen on button press.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package net.simplifiedcoding.retrofitexample.activities; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.api.APIService; import net.simplifiedcoding.retrofitexample.models.Result; import net.simplifiedcoding.retrofitexample.models.User; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button buttonSignIn, buttonSignUp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); buttonSignIn = (Button) findViewById(R.id.buttonSignIn); buttonSignUp = (Button) findViewById(R.id.buttonSignUp); buttonSignIn.setOnClickListener(this); buttonSignUp.setOnClickListener(this); } @Override public void onClick(View view) { if (view == buttonSignIn) { startActivity(new Intent(this, SignInActivity.class)); } else if (view == buttonSignUp) { startActivity(new Intent(this, SignUpActivity.class)); } } } |
- Now lets move to coding SignUpActivity.java.
Coding SignUpActivity
Creating Retrofit API
- Now first create a java interface inside api package and name it APIService.java. Here we will define all the API calls.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package net.simplifiedcoding.retrofitexample.api; import net.simplifiedcoding.retrofitexample.models.Result; import net.simplifiedcoding.retrofitexample.models.User; import retrofit2.Call; import retrofit2.Callback; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; /** * Created by Belal on 14/04/17. */ public interface APIService { //The register call @FormUrlEncoded @POST("register") Call<Result> createUser( @Field("name") String name, @Field("email") String email, @Field("password") String password, @Field("gender") String gender); } |
- So our API is ready. After @Field(“parameter”) we have the parameter value of our API that we created. So it should match with the parameter that is to be passed.
Creating Model
- Retrofit will automatically convert the response to our java object. For this we need to define the Model class. We will define the models inside the package named model that we created.
- Now first analyse the response that we are getting on register.
On Success
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "error": false, "message": "Registered successfully", "user": { "id": 8, "name": "Faiz Khan", "email": "faiz@gmail.com", "gender": "Male" } } |
On Failure
1 2 3 4 5 6 |
{ "error": true, "message": "This email already exist, please login" } |
- As you can see we have error, message and user in the response JSON. So we will create two class for this one will be the Result and other will be User.
- So create two classes inside your model package named Result.java and User.java and write the following codes.
- Result.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package net.simplifiedcoding.retrofitexample.models; import com.google.gson.annotations.SerializedName; /** * Created by Belal on 14/04/17. */ public class Result { @SerializedName("error") private Boolean error; @SerializedName("message") private String message; @SerializedName("user") private User user; public Result(Boolean error, String message, User user) { this.error = error; this.message = message; this.user = user; } public Boolean getError() { return error; } public String getMessage() { return message; } public User getUser() { return user; } } |
- User.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package net.simplifiedcoding.retrofitexample.models; /** * Created by Belal on 14/04/17. */ public class User { private int id; private String name; private String email; private String password; private String gender; public User(String name, String email, String password, String gender) { this.name = name; this.email = email; this.password = password; this.gender = gender; } public User(int id, String name, String email, String gender){ this.id = id; this.name = name; this.email = email; this.gender = gender; } public User(int id, String name, String email, String password, String gender) { this.id = id; this.name = name; this.email = email; this.password = password; this.gender = gender; } public int getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } public String getPassword(){ return password; } public String getGender() { return gender; } } |
Getting User Values
- Now come back go SignUpActivity.java and define the view objects and listeners.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package net.simplifiedcoding.retrofitexample.activities; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import net.simplifiedcoding.retrofitexample.R; public class SignUpActivity extends AppCompatActivity implements View.OnClickListener { private Button buttonSignUp; private EditText editTextName, editTextEmail, editTextPassword; private RadioGroup radioGender; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sign_up); buttonSignUp = (Button) findViewById(R.id.buttonSignUp); editTextName = (EditText) findViewById(R.id.editTextName); editTextEmail = (EditText) findViewById(R.id.editTextEmail); editTextPassword = (EditText) findViewById(R.id.editTextPassword); radioGender = (RadioGroup) findViewById(R.id.radioGender); buttonSignUp.setOnClickListener(this); } private void userSignUp(){ } @Override public void onClick(View view) { if (view == buttonSignUp) { userSignUp(); } } } |
- Now we will perform the SignUp using Retrofit.
Performing POST Request using Retrofit
- Inside the method userSignUp() write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
private void userSignUp() { //defining a progress dialog to show while signing up final ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage("Signing Up..."); progressDialog.show(); //getting the user values final RadioButton radioSex = (RadioButton) findViewById(radioGender.getCheckedRadioButtonId()); String name = editTextName.getText().toString().trim(); String email = editTextEmail.getText().toString().trim(); String password = editTextPassword.getText().toString().trim(); String gender = radioSex.getText().toString(); //building retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(APIUrl.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); //Defining retrofit api service APIService service = retrofit.create(APIService.class); //Defining the user object as we need to pass it with the call User user = new User(name, email, password, gender); //defining the call Call<Result> call = service.createUser( user.getName(), user.getEmail(), user.getPassword(), user.getGender() ); //calling the api call.enqueue(new Callback<Result>() { @Override public void onResponse(Call<Result> call, Response<Result> response) { //hiding progress dialog progressDialog.dismiss(); //displaying the message from the response as toast Toast.makeText(getApplicationContext(), response.body().getMessage(), Toast.LENGTH_LONG).show(); } @Override public void onFailure(Call<Result> call, Throwable t) { progressDialog.dismiss(); Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } |
Testing Sign Up Screen
- Run your application now and go to Sign Up Screen and try Signing Up as a new user.
- As you can see we got the success message. Check the MySQL database for the new user.
- See the last row. Bingo! it is working absolutely fine.
Making User Login after Successful Registration
- On the successful registration we will make the user automatically logged in. For this we will use the SharedPreferences to store user data.
- So we will create a new class for managing SharedPreferences.
- Create a new package named helper and inside the package create a class named SharedPrefManager.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package net.simplifiedcoding.retrofitexample.helper; import android.content.Context; import android.content.SharedPreferences; import net.simplifiedcoding.retrofitexample.models.User; /** * Created by Belal on 14/04/17. */ public class SharedPrefManager { private static SharedPrefManager mInstance; private static Context mCtx; private static final String SHARED_PREF_NAME = "simplifiedcodingsharedprefretrofit"; private static final String KEY_USER_ID = "keyuserid"; private static final String KEY_USER_NAME = "keyusername"; private static final String KEY_USER_EMAIL = "keyuseremail"; private static final String KEY_USER_GENDER = "keyusergender"; private SharedPrefManager(Context context) { mCtx = context; } public static synchronized SharedPrefManager getInstance(Context context) { if (mInstance == null) { mInstance = new SharedPrefManager(context); } return mInstance; } public boolean userLogin(User user) { SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt(KEY_USER_ID, user.getId()); editor.putString(KEY_USER_NAME, user.getName()); editor.putString(KEY_USER_EMAIL, user.getEmail()); editor.putString(KEY_USER_GENDER, user.getGender()); editor.apply(); return true; } public boolean isLoggedIn() { SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); if (sharedPreferences.getString(KEY_USER_EMAIL, null) != null) return true; return false; } public User getUser() { SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); return new User( sharedPreferences.getInt(KEY_USER_ID, 0), sharedPreferences.getString(KEY_USER_NAME, null), sharedPreferences.getString(KEY_USER_EMAIL, null), sharedPreferences.getString(KEY_USER_GENDER, null) ); } public boolean logout() { SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.clear(); editor.apply(); return true; } } |
- Now we need to modify the userSignUp() method of SignUpActivity.java as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
private void userSignUp() { //defining a progress dialog to show while signing up final ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage("Signing Up..."); progressDialog.show(); //getting the user values final RadioButton radioSex = (RadioButton) findViewById(radioGender.getCheckedRadioButtonId()); String name = editTextName.getText().toString().trim(); String email = editTextEmail.getText().toString().trim(); String password = editTextPassword.getText().toString().trim(); String gender = radioSex.getText().toString(); //building retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(APIUrl.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); //Defining retrofit api service APIService service = retrofit.create(APIService.class); //Defining the user object as we need to pass it with the call User user = new User(name, email, password, gender); //defining the call Call<Result> call = service.createUser( user.getName(), user.getEmail(), user.getPassword(), user.getGender() ); //calling the api call.enqueue(new Callback<Result>() { @Override public void onResponse(Call<Result> call, Response<Result> response) { //hiding progress dialog progressDialog.dismiss(); //displaying the message from the response as toast Toast.makeText(getApplicationContext(), response.body().getMessage(), Toast.LENGTH_LONG).show(); //if there is no error if (!response.body().getError()) { //starting profile activity finish(); SharedPrefManager.getInstance(getApplicationContext()).userLogin(response.body().getUser()); startActivity(new Intent(getApplicationContext(), HomeActivity.class)); } } @Override public void onFailure(Call<Result> call, Throwable t) { progressDialog.dismiss(); Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } |
- Now again test by registering as a new User and you should automatically log in on success.
- Now come to MainActivity.java and here we will also check if the user is already logged in then we will directly open the profile activity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package net.simplifiedcoding.retrofitexample.activities; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.api.APIService; import net.simplifiedcoding.retrofitexample.helper.SharedPrefManager; import net.simplifiedcoding.retrofitexample.models.Result; import net.simplifiedcoding.retrofitexample.models.User; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button buttonSignIn, buttonSignUp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //if user is already logged in openeing the profile activity if (SharedPrefManager.getInstance(this).isLoggedIn()) { finish(); startActivity(new Intent(this, HomeActivity.class)); } buttonSignIn = (Button) findViewById(R.id.buttonSignIn); buttonSignUp = (Button) findViewById(R.id.buttonSignUp); buttonSignIn.setOnClickListener(this); buttonSignUp.setOnClickListener(this); } @Override public void onClick(View view) { if (view == buttonSignIn) { startActivity(new Intent(this, SignInActivity.class)); } else if (view == buttonSignUp) { startActivity(new Intent(this, SignUpActivity.class)); } } } |
- Now run your application again and you will be directly navigated to the Home Activity. Now we will code the User Sign In Activity.
Coding Sign In Activity
Creating Retrofit API
- Again we will create a new API call inside APIService interface for the Sign In.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package net.simplifiedcoding.retrofitexample.api; import net.simplifiedcoding.retrofitexample.models.Result; import net.simplifiedcoding.retrofitexample.models.User; import retrofit2.Call; import retrofit2.Callback; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; /** * Created by Belal on 14/04/17. */ public interface APIService { @FormUrlEncoded @POST("register") Call<Result> createUser( @Field("name") String name, @Field("email") String email, @Field("password") String password, @Field("gender") String gender); //the signin call @FormUrlEncoded @POST("login") Call<Result> userLogin( @Field("email") String email, @Field("password") String password ); } |
Making User Sign In
- Now inside SignInActivity.java write the following code. We are doing almost the same we did in SignUpActivity.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package net.simplifiedcoding.retrofitexample.activities; import android.app.ProgressDialog; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton; import android.widget.Toast; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.api.APIService; import net.simplifiedcoding.retrofitexample.api.APIUrl; import net.simplifiedcoding.retrofitexample.helper.SharedPrefManager; import net.simplifiedcoding.retrofitexample.models.Result; import net.simplifiedcoding.retrofitexample.models.User; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class SignInActivity extends AppCompatActivity implements View.OnClickListener { private EditText editTextEmail, editTextPassword; private Button buttonSignIn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sign_in); editTextEmail = (EditText) findViewById(R.id.editTextEmail); editTextPassword = (EditText) findViewById(R.id.editTextPassword); buttonSignIn = (Button) findViewById(R.id.buttonSignIn); buttonSignIn.setOnClickListener(this); } private void userSignIn() { final ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage("Signing Up..."); progressDialog.show(); String email = editTextEmail.getText().toString().trim(); String password = editTextPassword.getText().toString().trim(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(APIUrl.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); APIService service = retrofit.create(APIService.class); Call<Result> call = service.userLogin(email, password); call.enqueue(new Callback<Result>() { @Override public void onResponse(Call<Result> call, Response<Result> response) { progressDialog.dismiss(); if (!response.body().getError()) { finish(); SharedPrefManager.getInstance(getApplicationContext()).userLogin(response.body().getUser()); startActivity(new Intent(getApplicationContext(), HomeActivity.class)); } else { Toast.makeText(getApplicationContext(), "Invalid email or password", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<Result> call, Throwable t) { progressDialog.dismiss(); Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } @Override public void onClick(View view) { if (view == buttonSignIn) { userSignIn(); } } } |
- Now we will code the home fragment where we will display a list of all users.
Coding Home Activity
- Every other screen is inside this activity using fragments so first we will define a FrameLayout where we will switch the fragments.
- So come inside content_home.xml and modify it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/content_home" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="net.simplifiedcoding.retrofitexample.activities.HomeActivity" tools:showIn="@layout/app_bar_home"> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> |
- Now open HomeActivity.java and modify it as below. For more details about this activity you can go through the Android Navigation Drawer Tutorial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
package net.simplifiedcoding.retrofitexample.activities; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.View; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.fragments.HomeFragment; import net.simplifiedcoding.retrofitexample.fragments.MessageFragment; import net.simplifiedcoding.retrofitexample.fragments.ProfileFragment; import net.simplifiedcoding.retrofitexample.helper.SharedPrefManager; public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private TextView textViewName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.setDrawerListener(toggle); toggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); if (!SharedPrefManager.getInstance(this).isLoggedIn()) { finish(); startActivity(new Intent(this, SignInActivity.class)); } View headerView = navigationView.getHeaderView(0); textViewName = (TextView) headerView.findViewById(R.id.textViewName); textViewName.setText(SharedPrefManager.getInstance(this).getUser().getName()); //loading home fragment by default displaySelectedScreen(R.id.nav_home); } @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } private void displaySelectedScreen(int itemId) { Fragment fragment = null; switch (itemId) { case R.id.nav_home: fragment = new HomeFragment(); break; case R.id.nav_profile: fragment = new ProfileFragment(); break; case R.id.nav_messages: fragment = new MessageFragment(); break; case R.id.nav_logout: logout(); break; } //replacing the fragment if (fragment != null) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.content_frame, fragment); ft.commit(); } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); } private void logout() { SharedPrefManager.getInstance(this).logout(); finish(); startActivity(new Intent(this, SignInActivity.class)); } @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { displaySelectedScreen(item.getItemId()); return true; } } |
Building Home Fragment
- In the Home Fragment we will display list of all the users from the database. For this we already have the API http://localhost/RetrofitExample/public/users. The response of this GET call is.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
{ "users": [ { "id": 1, "name": "Bhaque khan", "email": "bh@gmail.com", "gender": "Male" }, { "id": 2, "name": "Ramiz Khan", "email": "bela12@gmail.com", "gender": "Male" }, { "id": 3, "name": "Belal Khan", "email": "b1ela12@gmail.com", "gender": "Male" }, { "id": 4, "name": "Belal Khan", "email": "xyz@gmail.com", "gender": "Male" }, |
- To store it we will define one more model inside models package. So create a class named Users and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package net.simplifiedcoding.retrofitexample.models; import java.util.ArrayList; /** * Created by Belal on 14/04/17. */ public class Users { private ArrayList<User> users; public Users() { } public ArrayList<User> getUsers() { return users; } public void setUsers(ArrayList<User> users) { this.users = users; } } |
- Now we will build our Custom RecyclerView Adapter to display User List. So create a class named UserAdapter.java inside helper package and write the following code. If you need more detail about RecyclerView you can go through this RecyclerView Tutorial.Â
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
package net.simplifiedcoding.retrofitexample.helper; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.models.User; import java.util.List; import static android.R.id.list; /** * Created by Belal on 14/04/17. */ public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> { private List<User> users; private Context mCtx; public UserAdapter(List<User> users, Context mCtx) { this.users = users; this.mCtx = mCtx; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_users, parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(UserAdapter.ViewHolder holder, int position) { User user = users.get(position); holder.textViewName.setText(user.getName()); } @Override public int getItemCount() { return users.size(); } public class ViewHolder extends RecyclerView.ViewHolder { public TextView textViewName; public ImageButton imageButtonMessage; public ViewHolder(View itemView) { super(itemView); textViewName = (TextView) itemView.findViewById(R.id.textViewName); imageButtonMessage = (ImageButton) itemView.findViewById(R.id.imageButtonMessage); } } } |
- Now inside the fragments package create a new class named HomeFragment.java and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
package net.simplifiedcoding.retrofitexample.fragments; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.api.APIService; import net.simplifiedcoding.retrofitexample.api.APIUrl; import net.simplifiedcoding.retrofitexample.helper.UserAdapter; import net.simplifiedcoding.retrofitexample.models.Users; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * Created by Belal on 14/04/17. */ public class HomeFragment extends Fragment { private RecyclerView recyclerViewUsers; private RecyclerView.Adapter adapter; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_home, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); getActivity().setTitle("Home"); recyclerViewUsers = (RecyclerView) view.findViewById(R.id.recyclerViewUsers); recyclerViewUsers.setHasFixedSize(true); recyclerViewUsers.setLayoutManager(new LinearLayoutManager(getActivity())); Retrofit retrofit = new Retrofit.Builder() .baseUrl(APIUrl.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); APIService service = retrofit.create(APIService.class); Call<Users> call = service.getUsers(); call.enqueue(new Callback<Users>() { @Override public void onResponse(Call<Users> call, Response<Users> response) { adapter = new UserAdapter(response.body().getUsers(), getActivity()); recyclerViewUsers.setAdapter(adapter); } @Override public void onFailure(Call<Users> call, Throwable t) { } }); } } |
- Now come inside HomeActivity.java and modify the method displaySelectedScreen() as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
private void displaySelectedScreen(int itemId) { Fragment fragment = null; switch (itemId) { case R.id.nav_home: fragment = new HomeFragment(); break; case R.id.nav_profile: break; case R.id.nav_messages: break; case R.id.nav_logout: break; } //replacing the fragment if (fragment != null) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.content_frame, fragment); ft.commit(); } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); } |
- Now try running the application.
- So its working fine. Now we will code the functionality for the message icon, it should open an alert dialog to send message to the selected user. We already have the layout for the custom alert dialog.
Making Custom Alert Input for Sending Message
- Again come inside the UserAdapter.java class and modify it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
package net.simplifiedcoding.retrofitexample.helper; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import net.simplifiedcoding.retrofitexample.R; import net.simplifiedcoding.retrofitexample.models.User; import java.util.List; import static android.R.id.list; /** * Created by Belal on 14/04/17. */ public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> { private List<User> users; private Context mCtx; public UserAdapter(List<User> users, Context mCtx) { this.users = users; this.mCtx = mCtx; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_users, parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(UserAdapter.ViewHolder holder, int position) { final User user = users.get(position); holder.textViewName.setText(user.getName()); holder.imageButtonMessage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { LayoutInflater li = LayoutInflater.from(mCtx); View promptsView = li.inflate(R.layout.dialog_send_message, null); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mCtx); alertDialogBuilder.setView(promptsView); final EditText editTextTitle = (EditText) promptsView.findViewById(R.id.editTextTitle); final EditText editTextMessage = (EditText) promptsView.findViewById(R.id.editTextMessage); alertDialogBuilder .setCancelable(false) .setPositiveButton("Send", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { //getting the values String title = editTextTitle.getText().toString().trim(); String message = editTextMessage.getText().toString().trim(); //sending the message sendMessage(user.getId(), title, message); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.show(); } }); } //method to send message to the user private void sendMessage(int id, String title, String message){ } @Override public int getItemCount() { return users.size(); } public class ViewHolder extends RecyclerView.ViewHolder { public TextView textViewName; public ImageButton imageButtonMessage; public ViewHolder(View itemView) { super(itemView); textViewName = (TextView) itemView.findViewById(R.id.textViewName); imageButtonMessage = (ImageButton) itemView.findViewById(R.id.imageButtonMessage); } } } |
- Now try running the application and on the message icon click you will see an alert dialog asking for message inputs.
- Now we will code for sending the message. We already have API for this.
Sending Message to The User
- We are not using any FCM so the user will not be notified for the message, the recipient can only see the received message when he opens the app.
- First we will define a model for the send message api response. The response is.
1 2 3 4 5 6 |
{ "error": false, "message": "Message sent successfully" } |
- So inside models create a new java class named MessageResponse.java.
1 2 3 4 5 6 7 8 9 10 11 |