Tue. May 28th, 2024

Login API

URL: http://androindian.com/test/Login_raw.php

Request Type: POST

request:

{

“username”: “xyz@gmail.com”,

“password”: “1234”

}

Response:

{“key”:”Login Successfully”,”status”:”success”}

Add below gradle on build.gradle

Build.gradle

implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    api ("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2")

    //lifecycle components
    implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
    implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1")
    implementation ("androidx.lifecycle:lifecycle-common-java8:2.3.1")

activity_login.xml

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

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





            <EditText
                android:id="@+id/editTextEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="32dp"
                android:layout_marginEnd="32dp"
                android:hint="EMail"
                android:inputType="textEmailAddress"
                android:padding="12dp"
                android:textColor="@color/black"
                android:layout_marginTop="32dp"
                android:autofillHints="emailAddress" />

            <EditText
                android:id="@+id/editTextPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="32dp"
                android:layout_marginEnd="32dp"
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="12dp"
                android:textColor="@color/black"
                android:layout_marginTop="16dp"
                android:autofillHints="password" />

            <Button
                android:id="@+id/buttonLogin"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="32dp"
                android:layout_marginEnd="32dp"
                android:text="Login"
                android:textColor="@android:color/white"

                android:padding="12dp"
                android:layout_marginTop="32dp" />
        </LinearLayout>



        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:id="@+id/progress"
            android:visibility="gone"/>

    </RelativeLayout>

</layout>

Create one new package on name ui and move LoginActivity.kt in Ui

LoginActivity.kt

package com.example.mvvmreg

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmreg.databinding.ActivityMainBinding
import com.example.mvvmreg.helper.SharedHelper
import com.example.mvvmreg.helper.Utils
import com.example.mvvmreg.ui.DashboardView
import com.example.mvvmreg.viewmodel.LoginViewModel
import com.google.gson.JsonObject

class LoginActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: LoginViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        hideStatusBar()
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        setupViewModel()
        setupObserver()

        binding.buttonLogin.setOnClickListener {
            if (vaild()) {
                binding.progress.visibility = View.VISIBLE
                val requestObject = HashMap<String,String>()
                requestObject.put("username", binding.editTextEmail.text.toString())
                requestObject.put("password", binding.editTextPassword.text.toString())
                viewModel.doLogin(requestObject)
            }
        }


    }

    @Suppress("DEPRECATION")
    private fun hideStatusBar() {
        val decorView = window.decorView
        decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN

        supportActionBar?.hide()

        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
    }

    private fun setupObserver() {
        viewModel.loginResponse.observe(this) {
            binding.progress.visibility = View.GONE
            Toast.makeText(this@LoginActivity, "Login Successful", Toast.LENGTH_LONG)
                .show()
            startActivity(
                Intent(
                    this@LoginActivity,
                    DashboardView::class.java
                )
            )

        }
        viewModel.error.observe(this) {
            binding.progress.visibility = View.GONE
            Toast.makeText(this, it, Toast.LENGTH_LONG).show()
        }
    }

    private fun setupViewModel() {
        viewModel = ViewModelProvider(this)[LoginViewModel::class.java]
    }

    private fun vaild(): Boolean {
        if (TextUtils.isEmpty(binding.editTextEmail.text.toString())) {
            binding.editTextEmail.error = "Please enter email"
            return false
        }
        if (!Utils.isValidEmail(binding.editTextEmail.text.toString())) {
            binding.editTextEmail.error = "Please enter valid email"
            return false
        } else if (TextUtils.isEmpty(binding.editTextPassword.text.toString())) {
            binding.editTextPassword.error = "Please enter password"
            return false
        }
        return true
    }

}

Create one new package on name helper and create one class AppConstants.kt

AppConstants.kt

package com.example.mvvmreg.helper

import java.text.DecimalFormat

class AppConstants {

    companion object {
        
        const val SUCCESS = "Success"
        const val TOKEN_EXP = "Token Expired"
        const val BAD_REQUEST = "Bad Request"
        const val BAD_METHOD = "Bad Method"
        const val INTERNAL_ERROR = "Internal Server Error"
        const val UNAUTHORIZED = "Authorization Required"
        const val NOT_FOUND = "Not Found"
        const val GATEWAY_TIMEOUT = "Gateway Timed Out"


        object Responses {
            var SUCCESS = "SUCCESS"
        }
        
    }

}

NetworkState.kt

package com.example.mvvmreg.helper


class NetworkState(val status: Status, val msg: String) {
    enum class Status {
        RUNNING,
        SUCCESS,
        FAILED
    }

    companion object {
        var LOADED: NetworkState? = null
        var LOADING: NetworkState? = null

        init {
            LOADED = NetworkState(Status.SUCCESS, "Success")
            LOADING = NetworkState(Status.RUNNING, "Running")
        }
    }
}

SharedHelper.kt

package com.example.mvvmreg.helper

import android.content.Context
import android.content.SharedPreferences


object SharedHelper {
   public final var sharedPreferences: SharedPreferences? = null
    lateinit var editor: SharedPreferences.Editor;
    fun init(context: Context) {
        sharedPreferences = context.getSharedPreferences("Cache", Context.MODE_PRIVATE)
    }

    fun putKey(context: Context?, Key: String?, Value: String?) {
        // sharedPreferences = context.getSharedPreferences("Cache", Context.MODE_PRIVATE);
        editor = sharedPreferences!!.edit()
        editor.putString(Key, Value)
        editor.apply()
    }

    fun writeString(Key: String?, Value: String?) {
        editor = sharedPreferences!!.edit()
        editor.putString(Key, Value)
        editor.apply()
    }

    fun writeBoolean(context: Context?, Key: String?, Value: Boolean?) {
        //sharedPreferences = context.getSharedPreferences("Cache", Context.MODE_PRIVATE);
        editor = sharedPreferences!!.edit()
        editor.putBoolean(Key, Value!!)
        editor.apply()
    }

    fun getKey(contextGetKey: Context?, Key: String?): String? {
        //sharedPreferences = contextGetKey.getSharedPreferences("Cache", Context.MODE_PRIVATE);
        return sharedPreferences!!.getString(Key, "")
    }

    fun getString(key: String?): String? {
        return sharedPreferences!!.getString(key, null)
    }

    fun getBoolean(contextGetKey: Context?, Key: String?): Boolean {
        // sharedPreferences = contextGetKey.getSharedPreferences("Cache", Context.MODE_PRIVATE);
        return sharedPreferences!!.getBoolean(Key, false)
    }

    fun clearSharedPreferences(context: Context?) {
        //sharedPreferences = context.getSharedPreferences("Cache", Context.MODE_PRIVATE);
        sharedPreferences!!.edit().clear().apply()
    }

    object PreferredString {
        const val user_email = "user_email"

    }
}

URLHelper.kt

package com.example.mvvmreg.helper

class URLHelper {


    companion object{
        val base = " http://androindian.com/test/"
        val login = base + "Login_raw.php"
    }


}

Create one new package on name Model and create one class LoginResponseModel.kt

LoginResponseModel.kt

package com.example.mvvmreg.model

import com.google.gson.annotations.SerializedName

data class LoginResponseModel(
    val key: String,
    val status: String,
)

Create one new package on name network and create one class ApiClient.kt

ApiClient.kt

package com.example.mvvmreg.network

import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.QueryMap
import retrofit2.http.Url

interface ApiClient {

    @Headers("Content-Type:application/json")
    @POST
    fun getLoginResponse(@Url url: String?, @QueryMap body: HashMap<String,String>?): Call<JsonObject?>?
}

RetrofitClient.kt

package com.example.mvvmreg.network

import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.reflect.Modifier

class RetrofitClient {
    private  val BASE_URL = ""

    private val client: OkHttpClient =
        UnsafeOkHttpClient.unsafeOkHttpClient

    /*private static Gson gson = new GsonBuilder()
             .setLenient()
             .create();*/
    var builder2 = GsonBuilder().excludeFieldsWithModifiers(
        Modifier.FINAL,
        Modifier.TRANSIENT,
        Modifier.STATIC
    )
    var gson = builder2.create()
    private val builder: Retrofit.Builder = Retrofit.Builder()
        .client(client)
        .baseUrl(BASE_URL)
        .addConverterFactory(
            GsonConverterFactory.create(
                GsonBuilder()
                    .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
                    .excludeFieldsWithoutExposeAnnotation()
                    .serializeNulls()
                    .create()
            )
        )


    public val retrofit = builder.build()

    fun <S> createService(serviceClass: Class<S>?): S {
        return retrofit.create(serviceClass)
    }

}

ServiceGenerator.kt

package com.example.mvvmreg.network

import com.example.mvvmreg.helper.SharedHelper
import com.example.mvvmreg.helper.URLHelper
import com.google.gson.GsonBuilder
import okhttp3.Call
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

class ServiceGenerator {
    private val client1 = getClient()
    private val client: OkHttpClient = UnsafeOkHttpClient.unsafeOkHttpClient
    private val call: Call? = null
    private val url = getUrl()

    companion object{
        private var setToken: String? = null
    }

    private fun getUrl(): String {
        return URLHelper.base
    }

    var gson = GsonBuilder()
        .setLenient()
        .create()
    private val builder = Retrofit.Builder()
        .client(client)
        .baseUrl(url)
        .addConverterFactory(GsonConverterFactory.create(gson))
    private val retrofit = builder.build()

    fun <S> createService(serviceClass: Class<S>?): S {
        return retrofit.create(serviceClass)
    }


    private fun getClient(): OkHttpClient {
        val httpclient = OkHttpClient.Builder();
        httpclient.addInterceptor(Interceptor { chain: Interceptor.Chain ->
            val original = chain.request()
            val request = original.newBuilder()
                .header("X-Requested-With", "XMLHttpRequest")
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + SharedHelper.getString("access_token"))
                .method(original.method, original.body)
                .build()
            chain.proceed(request)
        })
        httpclient.connectTimeout(300, TimeUnit.SECONDS)
        return httpclient.build()
    }


    private fun getToken(): String? {
        return setToken
    }

    fun setSetToken(setToken: String) {
        ServiceGenerator.setToken = setToken
    }
}

UnsafeOkHttpClient.kt

package com.example.mvvmreg.network

import android.annotation.SuppressLint
import okhttp3.Interceptor
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSession


object UnsafeOkHttpClient {
    val unsafeOkHttpClient: OkHttpClient
        get() = try {
            // Create a trust manager that does not validate certificate chains
            @SuppressLint("CustomX509TrustManager") val trustAllCerts = arrayOf<TrustManager>(
                object : X509TrustManager {
                    @SuppressLint("TrustAllX509TrustManager")
                    override fun checkClientTrusted(
                        chain: Array<X509Certificate>,
                        authType: String
                    ) {
                    }

                    @SuppressLint("TrustAllX509TrustManager")
                    override fun checkServerTrusted(
                        chain: Array<X509Certificate>,
                        authType: String
                    ) {
                    }

                    override fun getAcceptedIssuers(): Array<X509Certificate> {
                        return arrayOf()
                    }
                }
            )

            // Install the all-trusting trust manager
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustAllCerts, SecureRandom())

            // Create an ssl socket factory with our all-trusting manager
            val sslSocketFactory = sslContext.socketFactory
            val builder = OkHttpClient.Builder()
            builder.cache(null)
            builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
            builder.hostnameVerifier(HostnameVerifier { hostname: String?, session: SSLSession? -> true })
            val interceptor = HttpLoggingInterceptor()
            interceptor.level = HttpLoggingInterceptor.Level.BODY
            builder.addInterceptor(interceptor)
            builder.addInterceptor(Interceptor { chain: Interceptor.Chain ->
                val original = chain.request()
                val request: Request.Builder = original.newBuilder()
                request.method(original.method, original.body)
                request.addHeader("Accept", "*/*")
                chain.proceed(request.build())
            })
            builder.connectTimeout(2, TimeUnit.MINUTES)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
            builder.build()
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
}

Create one new package on name network and create one class LoginRepository.kt

LoginRepository.kt

package com.example.mvvmreg.repository

import com.example.mvvmreg.helper.URLHelper
import com.example.mvvmreg.network.ApiClient
import com.example.mvvmreg.network.ServiceGenerator
import com.google.gson.JsonObject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import retrofit2.Call

class LoginRepository {
    private val retrofitClient = ServiceGenerator().createService(ApiClient::class.java)
    suspend fun doLogin(requestBody: HashMap<String,String>): Call<JsonObject?>? = withContext(Dispatchers.IO) {
        retrofitClient.getLoginResponse(URLHelper.login, requestBody)

    }

}

Create one new package on name Viewmodel and create one class LoginViewModel.kt

LoginViewModel.kt

package com.example.mvvmreg.viewmodel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mvvmreg.model.LoginResponseModel
import com.example.mvvmreg.repository.LoginRepository
import com.google.gson.Gson
import com.google.gson.JsonObject
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class LoginViewModel : ViewModel(){
    private val repository = LoginRepository()
    val loginResponse = MutableLiveData<LoginResponseModel>()
    val error = MutableLiveData<String>()
    fun doLogin(jsonObject: HashMap<String,String>) {
        viewModelScope.launch {
            val response = repository.doLogin(jsonObject)

            response?.enqueue(object: Callback<JsonObject?> {
                override fun onResponse(call: Call<JsonObject?>, response: Response<JsonObject?>) {
                    if (response.code() == 200) {
                        loginResponse.value = Gson().fromJson(response.body().toString(),LoginResponseModel::class.java)
                    } else {
                        error.value = "Invalid Login Credentials"
                    }
                }

                override fun onFailure(call: Call<JsonObject?>, t: Throwable) {
                    error.value = "Invalid Login Credentials"
                }

            })


        }
    }

}

MyApplication.kt

package com.example.mvvmreg

import android.app.Application
import com.example.mvvmreg.helper.SharedHelper

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        SharedHelper.init(this)
    }
}

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">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MvvmReg"
        android:usesCleartextTraffic="true"
        tools:targetApi="31">
        <activity
            android:name=".LoginActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.example.mvvmreg.ui.DashboardView" />
    </application>

</manifest>

By Rajashekar

I’m (Rajashekar) a core Android developer with complimenting skills as a web developer from India. I cherish taking up complex problems and turning them into beautiful interfaces. My love for decrypting the logic and structure of coding keeps me pushing towards writing elegant and proficient code, whether it is Android, PHP, Flutter or any other platforms. You would find me involved in cuisines, reading, travelling during my leisure hours.

Leave a Reply

Your email address will not be published. Required fields are marked *