Search code examples
androidkotlinretrofitandroid-jetpack-composedata-class

Setting up the logic for a Login Screen in Jetpack Compose


So for this project, I am setting up a Login Screen that logs into a database that is suppose to validate the email. It's my first time actually doing something like this. I have made a login page with firebase. But this time im tring to login to an Azure DB. Here are some code for better clarification

Here's the API

@Singleton
interface MMRApi {

    @FormUrlEncoded
    @POST(LOGIN_EP)
    suspend fun sendLoginInfo(
        @Field("EmailId") emailId: String,
        @Field("MobileMakeModel") mobileMake: String
    ): Response<LoginCreds>
}

Here's the Request and Response Fields

//Request Data Class    
data class LoginCreds(
        @SerializedName("EmailId") val emailId: String,
        @SerializedName("MobileMakeModel") var mobileModel: String
    ){
        companion object{
            const val PLATFORM = "Android"
        }
    
     private fun getModel(): String{
         return "$PLATFORM ${Build.MODEL}".also { mobileModel = it }
     }
    
    }

//Response Data Class

    data class LoginResponse(
        @SerializedName("data") val data: List<DataInfo>,
        @SerializedName("status") val status: Int,
        @SerializedName("message") val message: String
    ){
        override fun toString(): String {
            return "\n" +
                    "List of Data: $data" +
                    "Status: $status" +
                    "Message:$message"
        }
    }
    
    data class DataInfo(
        @SerializedName("Id") val id: Int,
        @SerializedName("CustomerId") val customerId: String,
        @SerializedName("UserAttributeId") val userAttributeId: Int,
        @SerializedName("Enabled") val enabled: Boolean
    ){
    
        override fun toString(): String {
            return "\n" +
                    "Id: $id" +
                    "CustomerId: $customerId" +
                    "UserAttributeId: $userAttributeId" +
                    "Enabled: $enabled"
        }
    
    }

The Repository

class MMRRepository @Inject constructor(private val api: MMRApi) {

    suspend fun postLogin(emailId: String, mobileMake: String): Response<LoginCreds>{
        return api.sendLoginInfo(emailId, mobileMake)
    }
}

The ViewModel

@HiltViewModel
class LoginViewModel @Inject constructor(private val repository: MMRRepository)
    : ViewModel() {

    val loginPostResponse: MutableLiveData<Response<LoginCreds>> = MutableLiveData()

    fun pushLogin(emailId: String, mobileMake: String){
        viewModelScope.launch(Dispatchers.IO) {
            val response = repository.postLogin(emailId, mobileMake)
            loginPostResponse.postValue(response)
        }
    }
}

The Composable

@Composable
fun MMRLoginScreen(navController: NavController, loginViewModel: LoginViewModel = hiltViewModel()){
    var emailId by rememberSaveable { mutableStateOf("") }

    Surface(modifier = Modifier
        .fillMaxSize()
        .padding(top = 65.dp)
    ) {
        Column(
            verticalArrangement = Arrangement.Top,
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Image(
                painter = painterResource(id = R.drawable.parabit_logo_orange_blue),
                contentDescription = stringResource(R.string.company_logo_string),
                contentScale = ContentScale.Fit,
                modifier = Modifier
                    .width(225.dp)
                    .height(95.dp)
                    .padding(top = 43.dp)
                )

            Text(
                text = "MMR Bluetooth Access Control",
                color = Color(0xFFF47B20),
                fontWeight = Bold,
                fontSize = 18.sp,
            )
            Spacer(modifier = Modifier.height(10.dp))

            LoginField(loading = false){
                loginViewModel.pushLogin(emailId = it, mobileMake = Build.MODEL)
                navController.navigate(MMRScreens.MainScreen.name)
            }

        }
    }

}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun LoginField(
    loading: Boolean = false,
    onDone: (String) -> Unit = {email ->}
){
    val email = rememberSaveable { mutableStateOf("") }
    val keyboardController = LocalSoftwareKeyboardController.current
    val valid = remember(email.value){
        email.value.trim().isNotEmpty()
    }

    Column(
        modifier = Modifier,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        EmailInput(
            emailState = email,
        )

        SubmitButton(
            textId = "Login",
            loading = loading,
            validInputs = valid
        ){
            onDone(email.value.trim())
            keyboardController?.hide()
        }
    }
}

@Composable
fun SubmitButton(
    textId: String,
    loading: Boolean,
    validInputs: Boolean,
    onClick: () -> Unit
) {

    Button(
        onClick = onClick,
        modifier = Modifier
            .padding(3.dp)
            .fillMaxWidth(),
        enabled = !loading && validInputs,
        colors = ButtonDefaults.buttonColors(Color(0xFFF47B20))
    ) {
        if (loading) CircularProgressIndicator(modifier = Modifier.size(25.dp))
        else Text(text = textId, modifier = Modifier.padding(5.dp))
    }

}

Any help is appreciated. Thank you.


Solution

  • So it was a simple. First i had to set up the API to send the two fields I need in order to get a response.

    @Singleton
    interface MMRApi {
        @FormUrlEncoded
        @POST(LOGIN_EP)
        suspend fun sendLoginInfo(
            @Field("EmailId") emailId: String,
            @Field("MobileMakeModel") mobileMake: String
        ): LoginResponse
    }
    

    The LoginResponse data class is used to store the response of the API once the input field goes through. Then for the instance, You set it up where the instance is used to tell you if the email worked.

    @Module
    @InstallIn(SingletonComponent::class)
    class AppModule {
    
        @Provides
        @Singleton
        fun provideLoginApi(): MMRApi{
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                    //Used to pick up the responses for the API Calls
                .client(
                        OkHttpClient.Builder().also { client ->
                            if (BuildConfig.DEBUG) {
                                val logging = HttpLoggingInterceptor()
                                logging.setLevel(HttpLoggingInterceptor.Level.BODY)
                                client.addInterceptor(logging)
                            }
                    }.connectTimeout(100, TimeUnit.SECONDS)
                            .readTimeout(100, TimeUnit.SECONDS)
                            .build()
                )
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(MMRApi::class.java)
        }
    
    
    
    } 
    

    Everything else was standard and I was able to get the Login Authentication to work