Tuesday, 19 January 2021

MVVM Architecture example using Retrofit Java

Model-View-VieModel (MVVM)

Model-View-ViewModel (MVVM) is a structural design pattern that separates objects into three distinct groups: Models hold application data. They're usually structs or simple classes. Views display visual elements and controls on the screen. They're typically subclasses of UIView.


build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

AppConstant.java

package com.mvvm_retrofit_java.constants;

/**
* get a api key from https://newsapi.org/
* just register an account and request for an api key
* when you will get an api key please replace with YOUR_API_KEY
*/

public class AppConstant {
public static final String BASE_URL = "https://newsapi.org/v2/";
public static final String API_KEY = "YOUR_API_KEY";
}

Article.java

package com.mvvm_retrofit_java.model;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Article {

@SerializedName("urlToImage")
@Expose
private String urlToImage;
@SerializedName("description")
@Expose
private String description;
@SerializedName("title")
@Expose
private String title;

public String getUrlToImage() {
return urlToImage;
}

public void setUrlToImage(String urlToImage) {
this.urlToImage = urlToImage;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

@Override
public String toString() {
return "BashboardNews{" +
"urlToImage='" + urlToImage + '\'' +
", description='" + description + '\'' +
", title='" + title + '\'' +
'}';
}

}

ArticleResponse.java

package com.mvvm_retrofit_java.response;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.mvvm_retrofit_java.model.Article;

import java.util.List;

public class ArticleResponse {
@SerializedName("articles")
@Expose
private List<Article> articles;

public List<Article> getArticles() {
return articles;
}

public void setArticles(List<Article> articles) {
this.articles = articles;
}

@Override
public String toString() {
return "BashboardNewsResponse{" +
"articles=" + articles +
'}';
}
}

RetrofitRequest.java

package com.mvvm_retrofit_java.retrofit;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import static com.mvvm_retrofit_java.constants.AppConstant.BASE_URL;

public class RetrofitRequest {

private static Retrofit retrofit;


public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

ApiRequest.java

package com.mvvm_retrofit_java.retrofit;

import com.mvvm_retrofit_java.response.ArticleResponse;

import retrofit2.Call;
import retrofit2.http.GET;

import static com.mvvm_retrofit_java.constants.AppConstant.API_KEY;

public interface ApiRequest {

@GET("top-headlines?country=de&category=business&apiKey="+API_KEY)
Call<ArticleResponse> getTopHeadlines();

}

ArticleRepository.java

package com.mvvm_retrofit_java.repository;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.mvvm_retrofit_java.response.ArticleResponse;
import com.mvvm_retrofit_java.retrofit.ApiRequest;
import com.mvvm_retrofit_java.retrofit.RetrofitRequest;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class ArticleRepository {
private static final String TAG = ArticleRepository.class.getSimpleName();
private final ApiRequest apiRequest;

public ArticleRepository() {
apiRequest = RetrofitRequest.getRetrofitInstance().create(ApiRequest.class);
}

public LiveData<ArticleResponse> getDashBoardNews() {
final MutableLiveData<ArticleResponse> data = new MutableLiveData<>();
apiRequest.getTopHeadlines()
.enqueue(new Callback<ArticleResponse>() {


@Override
public void onResponse(@NonNull Call<ArticleResponse> call,
@NonNull Response<ArticleResponse> response) {
Log.d(TAG, "onResponse response:: " + response);



if (response.body() != null) {
data.setValue(response.body());

Log.d(TAG, "articles total result:: " + response.body());
Log.d(TAG, "size:: " + response.body().getArticles().size());
// Log.d(TAG, "articles title pos 0:: " + response.body().getArticles().get(0).getTitle());
}
}

@Override
public void onFailure(@NonNull Call<ArticleResponse> call, @NonNull Throwable t) {
data.setValue(null);
}
});
return data;
}
}

ArticleViewModel.java

package com.mvvm_retrofit_java.view_model;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import com.mvvm_retrofit_java.repository.ArticleRepository;
import com.mvvm_retrofit_java.response.ArticleResponse;

public class ArticleViewModel extends AndroidViewModel {

private ArticleRepository articleResponse;
private LiveData<ArticleResponse> articleResponseLiveData;

public ArticleViewModel(@NonNull Application application) {
super(application);

articleResponse = new ArticleRepository();
this.articleResponseLiveData = articleResponse.getDashBoardNews();
}

public LiveData<ArticleResponse> getBashboardNewsResponseLiveData() {
return articleResponseLiveData;
}
}

MovieArticleAdapter.java

package com.mvvm_retrofit_java.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.mvvm_retrofit_java.R;
import com.mvvm_retrofit_java.model.Article;

import java.util.ArrayList;

public class MovieArticleAdapter extends RecyclerView.Adapter<MovieArticleAdapter.ViewHolder> {

private final Context context;
ArrayList<Article> articleArrayList;

public MovieArticleAdapter(Context context, ArrayList<Article> articleArrayList) {
this.context = context;
this.articleArrayList = articleArrayList;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_each_article,viewGroup,false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
Article article = articleArrayList.get(i);
viewHolder.tvTitle.setText(article.getTitle());
Glide.with(context)
.load(article.getUrlToImage())
.into(viewHolder.imgViewCover);
}

@Override
public int getItemCount() {
return articleArrayList.size();
}

public static class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView imgViewCover;
private final TextView tvTitle;

public ViewHolder(@NonNull View itemView) {
super(itemView);

imgViewCover = itemView.findViewById(R.id.imgViewCover);
tvTitle = itemView.findViewById(R.id.tvTitle);
}
}
}

MainActivity.java

package com.mvvm_retrofit_java.view;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;

import com.mvvm_retrofit_java.R;
import com.mvvm_retrofit_java.adapter.MovieArticleAdapter;
import com.mvvm_retrofit_java.model.Article;
import com.mvvm_retrofit_java.view_model.ArticleViewModel;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private static final String TAG = MainActivity.class.getSimpleName();

private RecyclerView recycler_view;
private ProgressBar progress_bar;

private LinearLayoutManager layoutManager;
private ArrayList<Article> articleArrayList = new ArrayList<>();
ArticleViewModel articleViewModel;
private MovieArticleAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

init();

getArticles();
}

private void init() {
progress_bar = findViewById(R.id.progress_bar);
recycler_view = findViewById(R.id.recycler_view);

// use a linear layout manager
layoutManager = new LinearLayoutManager(MainActivity.this);
recycler_view.setLayoutManager(layoutManager);

// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recycler_view.setHasFixedSize(true);

// adapter
adapter = new MovieArticleAdapter(MainActivity.this, articleArrayList);
recycler_view.setAdapter(adapter);

// View Model
articleViewModel = ViewModelProviders.of(this).get(ArticleViewModel.class);
}

private void getArticles() {
articleViewModel.getBashboardNewsResponseLiveData().observe(this, articleResponse -> {
if (articleResponse != null && articleResponse.getArticles() != null
&& !articleResponse.getArticles().isEmpty()) {
progress_bar.setVisibility(View.GONE);
List<Article> articleList = articleResponse.getArticles();
articleArrayList.addAll(articleList);
adapter.notifyDataSetChanged();
}
});
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context=".view.MainActivity">

<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>


</RelativeLayout>

list_each_article.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/imgViewCover"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginBottom="5dp"
android:scaleType="centerCrop" />


<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/transparent">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textStyle="bold"
android:padding="16dp"/>
</RelativeLayout>

</RelativeLayout>


You can work on to enhance and customize to your requirements. You can also get the project here.










Saturday, 21 December 2019

Android (Kotlin) Login and Register using Retrofit example with PHP MYSQL

Android (Kotlin) Login and Register example with Retrofit


Output : 




Create Table :



PHP Files :

config.php

<?php
$host="localhost";
$user="root";
$password="";
$db = "kotlin_example";
$con = mysqli_connect($host,$user,$password,$db);
// Check connection
if (mysqli_connect_errno())
  {
  echo "Failed to connect to MySQL: " . mysqli_connect_error();
  }else{  //echo "Connect"; 

   }
?>

register.php

<?php

if($_SERVER['REQUEST_METHOD']=='POST'){
    include_once("config.php");
    $postdata = file_get_contents("php://input");

    if (isset($postdata)) {
        $request1 = json_decode($postdata);
        $email = $request1->email;
        $username = $request1->username;
        $password = $request1->password;
         if($email == '' || $username == '' || $password == ''){
                echo json_encode(array( "status" => "false","message" => "Parameter missing!") );
         }else{
                $query= "SELECT * FROM user WHERE username='$username'";
                $result= mysqli_query($con, $query);
                if(mysqli_num_rows($result) > 0){  
                   echo json_encode(array( "status" => "false","message" => "Username already exist!") );
                }else{ 
                 $query = "INSERT INTO user (email,username,password) VALUES ('$email','$username','$password')";
                 if(mysqli_query($con,$query)){
                     $query= "SELECT * FROM user WHERE username='$username'";
                             $result= mysqli_query($con, $query);
                         $emparray = array();
                             if(mysqli_num_rows($result) > 0){  
                             while ($row = mysqli_fetch_assoc($result)) {
                                         $emparray[] = $row;
                                         echo json_encode(array( "status" => "true","message" => "Successfully registered!" , "data" => $row) );
                                       }
                             }
                 }else{
                     echo json_encode(array( "status" => "false","message" => "Error occured, please try again!") );
                }
            }
                    mysqli_close($con);
         }
    }

} else{
    echo json_encode(array( "status" => "false","message" => "Error occured, please try again!") );
}

 ?>

login.php

<?php

if($_SERVER['REQUEST_METHOD']=='POST'){
include_once("config.php");
$postdata = file_get_contents("php://input");
    if (isset($postdata)) {
        $request1 = json_decode($postdata);
        $email = $request1->email;
        $password = $request1->password;
        if( $email == '' || $password == '' ){
            echo json_encode(array( "status" => false,"message" => "Parameter missing!") );
         }else{
            $query= "SELECT * FROM user WHERE email='$email' AND password='$password'";
                $result= mysqli_query($con, $query);
                if(mysqli_num_rows($result) > 0){  
                 $query= "SELECT * FROM user WHERE email='$email' AND password='$password'";
                             $result= mysqli_query($con, $query);
                         $emparray;
                             if(mysqli_num_rows($result) > 0){  
                             while ($row = mysqli_fetch_assoc($result)) {
                                         $emparray = $row;
                                       }
                             }
                   echo json_encode(array( "status" => true,"message" => "Login successfully!", "data" => $emparray) );
                }else{ 
                    echo json_encode(array( "status" => false,"message" => "Invalid email or password!") );
                }
                 mysqli_close($con);
         }
    }
} else{
    echo json_encode(array( "status" => false,"message" => "Error occured, please try again!") );
}

?>

get_user_detail.php

<?php
$response = array();
include_once("config.php");
if (isset($_GET["id"])) {
    $id = $_GET['id'];
    $query= "SELECT * FROM user WHERE id='$id'";
    $result= mysqli_query($con, $query);
    $emparray;
    if(mysqli_num_rows($result) > 0){  
        while ($row = mysqli_fetch_assoc($result)) {
            $emparray = $row;
            echo json_encode(array( "status" => true, "data" => $emparray) );
        }
    }
    else{ 
        echo json_encode(array( "status" => false,"message" => "No User Found!") );
    }
}else {
    echo json_encode(array( "status" => false,"message" => "Required field(s) is missing!") )
}

?>

build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
implementation 'com.google.android.material:material:1.0.0'

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.hpos.loginregisterusingretrofit">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:usesCleartextTraffic="true"
        tools:ignore="GoogleAppIndexingWarning"
        tools:targetApi="m">
        <activity android:name=".HomeActivity"></activity>
        <activity android:name=".RegisterActivity" />
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Models

SigninRequest.kt

package com.hpos.loginregisterusingretrofit.models

import com.google.gson.annotations.SerializedName

class SigninRequest(@SerializedName("email") var email: String,
                    @SerializedName("password") var password: String)

SigninResponse.kt

package com.hpos.loginregisterusingretrofit.models

class SigninResponse(val status: Boolean, val message:String, val data: User)

SignupRequest.kt

package com.hpos.loginregisterusingretrofit.models

import com.google.gson.annotations.SerializedName

class SignupRequest(@SerializedName("email") var email: String,
                    @SerializedName("username") var username: String,
                    @SerializedName("password") var password: String)

SignupResponse.kt

package com.hpos.loginregisterusingretrofit.models

class SignupResponse(val status: Boolean, val message:String, val data: User)

UserResponse.kt

package com.hpos.loginregisterusingretrofit.models

data class UserResponse(val status: Boolean, val data: User)

User.kt

package com.hpos.loginregisterusingretrofit.models;

data class User(val id:Int, val username:String, val email:String, val password:String)

Retrofit Files

ApiList

package com.hpos.loginregisterusingretrofit.api

import com.hpos.loginregisterusingretrofit.models.*
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query

interface ApiList {

    //TODO : Register User
    @POST("register.php")
    fun doRegister(
        @Body signupRequest: SignupRequest
    ): Call<SignupResponse> // body data

    //TODO : Login User
    @POST("login.php")
    fun doLogin(
        @Body signinRequest: SigninRequest
    ): Call<SigninResponse> // body data

    //TODO : Get User
    @GET("get_user_detail.php")
    fun getUser(@Query("id") id: String): Call<UserResponse>
}


ApiService

package com.hpos.loginregisterusingretrofit.api

import retrofit2.Retrofit

object ApiService {
    private val TAG = "--ApiService"

    private const val BASE_URL = "http://192.168.1.111/KotlinExample/LoginRegistration/"

    fun loginApiCall() = Retrofit.Builder()
            .baseUrl(BASE_URL)
//            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(ApiWorker.gsonConverter)
            .client(ApiWorker.client)
            .build()
            .create(ApiList::class.java)!!
}

ApiWorker

package com.hpos.loginregisterusingretrofit.api

import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.converter.gson.GsonConverterFactory
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.util.concurrent.TimeUnit

object ApiWorker {
    private var mClient: OkHttpClient? = null
    private var mGsonConverter: GsonConverterFactory? = null

    /**
     * Don't forget to remove Interceptors (or change Logging Level to NONE)
     * in production! Otherwise people will be able to see your request and response on Log Cat.
     */
    val client: OkHttpClient
        @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
        get() {
            if (mClient == null) {
                val interceptor = HttpLoggingInterceptor()
                interceptor.level = HttpLoggingInterceptor.Level.BODY

                val httpBuilder = OkHttpClient.Builder()
                httpBuilder
                    .connectTimeout(120, TimeUnit.SECONDS)
                    .readTimeout(120, TimeUnit.SECONDS)
                    .addInterceptor(interceptor)  /// show all JSON in logCat
                    .addInterceptor { chain ->
                        val original = chain.request()

                        val requestBuilder = original.newBuilder()
//                        .addHeader("Authorization", AUTH)
                            .addHeader("Content-Type", "application/json")
//                            .method(original.method(), original.body())

                        val request = requestBuilder.build()
                        chain.proceed(request)
                    }
                mClient = httpBuilder.build()

            }
            return mClient!!
        }


    val gsonConverter: GsonConverterFactory
        get() {
            if (mGsonConverter == null) {
                mGsonConverter = GsonConverterFactory
                    .create(
                        GsonBuilder()
                            .setLenient()
                            .disableHtmlEscaping()
                            .create()
                    )
            }
            return mGsonConverter!!
        }
}

Screens Files

HomeActivity.kt

package com.hpos.loginregisterusingretrofit

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import android.widget.Toast
import com.hpos.loginregisterusingretrofit.api.ApiService
import com.hpos.loginregisterusingretrofit.models.User
import com.hpos.loginregisterusingretrofit.models.UserResponse
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class HomeActivity : AppCompatActivity() {

    var userId: String = "";

    private lateinit var txt_name: TextView
    private lateinit var txt_email: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        setTitle("Home")

        val intent = getIntent()
        userId = intent.getIntExtra("id", 0).toString()

        txt_name = findViewById(R.id.txt_name) as TextView
        txt_email = findViewById(R.id.txt_email) as TextView

        getUser()

    }

    private fun getUser() {
        ApiService.loginApiCall().getUser(userId).enqueue(object : Callback<UserResponse> {
            override fun onResponse(
                call: Call<UserResponse>,
                response: Response<UserResponse>) {
                Log.d("Response User ::::", response.body().toString())
                if (response.body()!!.status){
                    txt_name.setText(response.body()!!.data.username)
                    txt_email.setText(response.body()!!.data.email)
                }
            }

            override fun onFailure(call: Call<UserResponse>, t: Throwable) {
//                            Log.d("error::::",t?.message)
            }

        })
    }
}


activity_home.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HomeActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_centerInParent="true">

        <androidx.appcompat.widget.AppCompatImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/my"
            android:layout_gravity="center_horizontal"/>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="10dp"
            android:layout_gravity="center_horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Name : "
                android:textSize="20sp"
                android:textStyle="bold"/>

            <TextView
                android:id="@+id/txt_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Jainish Prajapati"
                android:textSize="20sp"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="10dp"
            android:layout_gravity="center_horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Email : "
                android:textSize="20sp"
                android:textStyle="bold"/>

            <TextView
                android:id="@+id/txt_email"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="jainishprajapati17@gmail.com"
                android:textSize="20sp"/>

        </LinearLayout>

    </LinearLayout>

</RelativeLayout>

LoginActivity.kt

package com.hpos.loginregisterusingretrofit

import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.widget.AppCompatTextView
import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputEditText
import com.hpos.loginregisterusingretrofit.api.ApiService
import com.hpos.loginregisterusingretrofit.models.SigninRequest
import com.hpos.loginregisterusingretrofit.models.SigninResponse
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class LoginActivity : Activity(), View.OnClickListener {

    private lateinit var ed_email : TextInputEditText
    private lateinit var ed_password : TextInputEditText
    private lateinit var btn_signin : MaterialButton
    private lateinit var txt_sign_up : AppCompatTextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        ed_email = findViewById(R.id.ed_email) as TextInputEditText
        ed_password = findViewById(R.id.ed_password) as TextInputEditText
        btn_signin = findViewById(R.id.btn_signin) as MaterialButton
        txt_sign_up = findViewById(R.id.txt_sign_up) as AppCompatTextView

        btn_signin.setOnClickListener(this)
        txt_sign_up.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v?.id){
            R.id.txt_sign_up -> {
                startActivity(Intent(this@LoginActivity, RegisterActivity::class.java))
            }
            R.id.btn_signin -> {
                if (validation()) {
                    val json = JSONObject()
                    json.put("email", ed_email.text.toString())
                    json.put("password", ed_password.text.toString())

                    ApiService.loginApiCall().doLogin(
                        SigninRequest(
                            ed_email.text.toString(),ed_password.text.toString()
                        )
                    ).enqueue(object : Callback<SigninResponse> {
                        override fun onResponse(
                            call: Call<SigninResponse>,
                            response: Response<SigninResponse>) {

                            Log.d("Response::::", response.body().toString())
                            if (response.body()!!.status){
                                finish()
                                val intent = Intent(this@LoginActivity, HomeActivity::class.java)
                                intent.putExtra("id",response.body()!!.data.id)
                                startActivity(intent)
                            }else{
                                Toast.makeText(applicationContext, response.body()!!.message, Toast.LENGTH_LONG).show()
                            }
                        }

                        override fun onFailure(call: Call<SigninResponse>, t: Throwable) {
                        }

                    })
                }
            }
        }
    }

    fun validation(): Boolean {
        var value = true

        val password = ed_password.text.toString().trim()
        val name = ed_email.text.toString().trim()

        if (password.isEmpty()) {
            ed_password.error = "Password required"
            ed_password.requestFocus()
            value = false
        }

        if (name.isEmpty()) {
            ed_email.error = "Email required"
            ed_email.requestFocus()
            value = false
        }

        return value;
    }
}


activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_margin="20dp"
        android:layout_centerInParent="true">

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/signin"
            android:textColor="@android:color/black"
            android:textStyle="bold"
            android:textSize="36sp"/>

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/welcome_back"
            android:textSize="20sp"
            android:textColor="@android:color/darker_gray"/>

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/email"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_marginTop="30dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/ed_email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawablePadding="5dp"
                android:inputType="textEmailAddress"/>
        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_marginTop="10dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/ed_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawablePadding="5dp"
                android:inputType="textPassword"/>

        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btn_signin"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/sign_in"
            android:padding="15dp"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_horizontal"
            android:layout_marginTop="20dp">
            <androidx.appcompat.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/don_t_have_an_account"
                android:textSize="20sp"
                android:textColor="@android:color/darker_gray"/>
            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/txt_sign_up"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/sign_up_here"
                android:textSize="20sp"
                android:textColor="@android:color/black"
                android:layout_marginStart="20dp"/>
        </LinearLayout>



    </LinearLayout>

</RelativeLayout>

RegisterActivity.kt

package com.hpos.loginregisterusingretrofit

import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputEditText
import com.hpos.loginregisterusingretrofit.api.ApiService
import com.hpos.loginregisterusingretrofit.models.SignupRequest
import com.hpos.loginregisterusingretrofit.models.SignupResponse
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class RegisterActivity : Activity(), View.OnClickListener {

    private lateinit var ed_username: TextInputEditText
    private lateinit var ed_email: TextInputEditText
    private lateinit var ed_password: TextInputEditText
    private lateinit var btn_signup: MaterialButton

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)

        ed_username = findViewById(R.id.ed_username) as TextInputEditText
        ed_email = findViewById(R.id.ed_email) as TextInputEditText
        ed_password = findViewById(R.id.ed_password) as TextInputEditText
        btn_signup = findViewById(R.id.btn_signup) as MaterialButton

        btn_signup.setOnClickListener(this)

    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_signup -> {
                if (validation()) {
                    val json = JSONObject()
                    json.put("email", ed_email.text.toString())
                    json.put("username", ed_username.text.toString())
                    json.put("password", ed_password.text.toString())

                    ApiService.loginApiCall().doRegister(
                        SignupRequest(
                            ed_email.text.toString(),
                            ed_username.text.toString(), ed_password.text.toString()
                        )
                    ).enqueue(object : Callback<SignupResponse> {
                        override fun onResponse(
                            call: Call<SignupResponse>,
                            response: Response<SignupResponse>
                        ) {

                            Log.d("Response::::", response.body().toString())
                            val loginResponse :  SignupResponse
                            loginResponse = response.body()!!
                            if (loginResponse.status){
                                finish()
                            }else{
                                Toast.makeText(applicationContext, response.body()!!.message, Toast.LENGTH_LONG).show()
                            }
                        }

                        override fun onFailure(call: Call<SignupResponse>, t: Throwable) {
                        }

                    })
                }
            }
        }
    }


    fun validation(): Boolean {
        var value = true

        val email = ed_email.text.toString().trim()
        val password = ed_password.text.toString().trim()
        val name = ed_username.text.toString().trim()

        if (email.isEmpty()) {
            ed_email.error = "Email required"
            ed_email.requestFocus()
            value = false
        }


        if (password.isEmpty()) {
            ed_password.error = "Password required"
            ed_password.requestFocus()
            value = false
        }

        if (name.isEmpty()) {
            ed_username.error = "Name required"
            ed_username.requestFocus()
            value = false
        }

        return value;
    }
}


activity_register.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity">

    <androidx.appcompat.widget.AppCompatImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_margin="20dp"
        android:layout_centerInParent="true">

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/signup"
            android:textColor="@android:color/black"
            android:textStyle="bold"
            android:textSize="36sp"/>

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/user_name"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_marginTop="30dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/ed_username"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawablePadding="5dp" />
        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/email"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_marginTop="10dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/ed_email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawablePadding="5dp" />
        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_marginTop="10dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/ed_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawablePadding="5dp"
                android:inputType="textPassword"/>

        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btn_signup"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/sign_up"
            android:padding="15dp"/>

    </LinearLayout>

</RelativeLayout>

style.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

string.xml

<resources>
    <string name="app_name">LoginRegisterUsingRetrofit</string>
    <string name="user_name">User Name</string>
    <string name="password">Password</string>
    <string name="sign_in">Sign In</string>
    <string name="signin">Signin</string>
    <string name="welcome_back">Welcome back</string>
    <string name="sign_up">Sign Up</string>
    <string name="email">Email</string>
    <string name="signup">Signup</string>
    <string name="don_t_have_an_account">Don\'t have an account?</string>
    <string name="sign_up_here">Sign up here</string>
</resources>


color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#5edb92</color>
    <color name="colorPrimaryDark">#5edb92</color>
    <color name="colorAccent">#5edb92</color>

    <color name="colorPrimaryBackground">#0886c7</color>
    <color name="colorFormBackground">#e4e4e4</color>
</resources>