Course Content
Expense Manager App – Login
0/1
Expense Manager App – Product
0/1
Expense Manager Android App
    About Lesson

    Here’s a detailed guide on implementing a login feature in an Android Expense Manager app using Retrofit for network requests. This guide covers creating the UI, setting up the necessary classes for handling API requests, and integrating them into the app.

     

    Login Feature for Expense Manager Android App Using Retrofit

    The login feature allows users to authenticate with the app, ensuring secure access to personalized data. This implementation includes creating a login screen, handling API requests with Retrofit, and managing user sessions.


    1. Create the Login Screen Layout

    Create the login screen layout (activity_login.xml) with input fields for the username, password, and a button to initiate the login.

    <?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activities.login.LoginActivity"
    android:background="@color/white_pressed"
    android:padding="@dimen/padding_12">

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/padding_12"
    android:orientation="vertical"
    android:layout_marginTop="@dimen/margin_48"
    android:background="@drawable/login_card_bg">

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/welcome_back"
    android:textSize="@dimen/font_size_xlarge"
    style="@style/TextViewMediumFont"
    android:textColor="@color/black_333333"/>

    <com.google.android.material.textfield.TextInputLayout
    android:id="@+id/til_email"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:boxStrokeWidth="0dp"
    app:boxStrokeWidthFocused="0dp"
    android:layout_marginTop="@dimen/margin_16">

    <EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/email"
    android:textSize="@dimen/font_size_medium"
    android:background="@drawable/edittext_bg"
    style="@style/EditTextRegularFont"
    android:inputType="textEmailAddress"
    android:maxLines="1"/>

    <requestFocus/>

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

    <com.google.android.material.textfield.TextInputLayout
    android:id="@+id/til_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/margin_12"
    app:boxStrokeWidth="0dp"
    app:boxStrokeWidthFocused="0dp">

    <EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/password"
    android:textSize="@dimen/font_size_medium"
    android:background="@drawable/edittext_bg"
    style="@style/EditTextRegularFont"
    android:inputType="textPassword"
    android:maxLines="1" />

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

    <TextView
    android:id="@+id/btn_login"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/margin_12"
    android:text="@string/login"
    android:textAllCaps="false"
    android:textSize="@dimen/font_size_medium"
    android:textColor="@color/white"
    android:textAlignment="center"
    android:background="@drawable/login_button_bg"
    style="@style/TextViewMediumFont"/>

    </LinearLayout>

    </LinearLayout>

     


    2. Define the LoginRequest and LoginResponse Models

    Define the data models for the login request and response. These classes will handle the data sent to and received from the API.

    Implement the LoginModel class.

    package in.infovistar.expensemanager.models;

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

    public class LoginModel {
    @SerializedName("status")
    @Expose
    private Boolean status;
    @SerializedName("message")
    @Expose
    private String message;
    @SerializedName("result")
    @Expose
    private LoginResultModel result;

    public Boolean getStatus() {
    return status;
    }

    public void setStatus(Boolean status) {
    this.status = status;
    }

    public String getMessage() {
    return message;
    }

    public void setMessage(String message) {
    this.message = message;
    }

    public LoginResultModel getResult() {
    return result;
    }

    public void setResult(LoginResultModel result) {
    this.result = result;
    }

    }

     


    Implement the LoginResultModel class.
    package in.infovistar.expensemanager.models;

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

    public class LoginResultModel {

    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("name")
    @Expose
    private String name;
    @SerializedName("email")
    @Expose
    private String email;
    @SerializedName("password")
    @Expose
    private String password;
    @SerializedName("country_code")
    @Expose
    private String countryCode;
    @SerializedName("mobile_number")
    @Expose
    private String mobileNumber;
    @SerializedName("branch_id")
    @Expose
    private String branchId;
    @SerializedName("image_url")
    @Expose
    private String imageUrl;
    @SerializedName("role")
    @Expose
    private String role;
    @SerializedName("access_group_id")
    @Expose
    private String accessGroupId;
    @SerializedName("city")
    @Expose
    private String city;
    @SerializedName("secret_token")
    @Expose
    private String secretToken;
    @SerializedName("ip_address")
    @Expose
    private String ipAddress;
    @SerializedName("created_at")
    @Expose
    private String createdAt;
    @SerializedName("updated_at")
    @Expose
    private String updatedAt;
    @SerializedName("status")
    @Expose
    private String status;

    public String getId() {
    return id;
    }

    public void setId(String id) {
    this.id = id;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public String getEmail() {
    return email;
    }

    public void setEmail(String email) {
    this.email = email;
    }

    public String getPassword() {
    return password;
    }

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

    public String getCountryCode() {
    return countryCode;
    }

    public void setCountryCode(String countryCode) {
    this.countryCode = countryCode;
    }

    public String getMobileNumber() {
    return mobileNumber;
    }

    public void setMobileNumber(String mobileNumber) {
    this.mobileNumber = mobileNumber;
    }

    public String getBranchId() {
    return branchId;
    }

    public void setBranchId(String branchId) {
    this.branchId = branchId;
    }

    public String getImageUrl() {
    return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;
    }

    public String getRole() {
    return role;
    }

    public void setRole(String role) {
    this.role = role;
    }

    public String getAccessGroupId() {
    return accessGroupId;
    }

    public void setAccessGroupId(String accessGroupId) {
    this.accessGroupId = accessGroupId;
    }

    public String getCity() {
    return city;
    }

    public void setCity(String city) {
    this.city = city;
    }

    public String getSecretToken() {
    return secretToken;
    }

    public void setSecretToken(String secretToken) {
    this.secretToken = secretToken;
    }

    public String getIpAddress() {
    return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
    this.ipAddress = ipAddress;
    }

    public String getCreatedAt() {
    return createdAt;
    }

    public void setCreatedAt(String createdAt) {
    this.createdAt = createdAt;
    }

    public String getUpdatedAt() {
    return updatedAt;
    }

    public void setUpdatedAt(String updatedAt) {
    this.updatedAt = updatedAt;
    }

    public String getStatus() {
    return status;
    }

    public void setStatus(String status) {
    this.status = status;
    }
    }


    3. Define the ApiService Interface

    package in.infovistar.expensemanager.api;

    import java.util.HashMap;

    import in.infovistar.expensemanager.models.CustomerModel;
    import in.infovistar.expensemanager.models.LoginModel;
    import retrofit2.Call;
    import retrofit2.http.FieldMap;
    import retrofit2.http.FormUrlEncoded;
    import retrofit2.http.POST;

    public interface ApiService {

    @FormUrlEncoded
    @POST("login")
    Call<LoginModel> getLogin(@FieldMap HashMap<String, Object> hashMap);

    }

     


    4. Create the LoginActivity

    Implement the LoginActivity class, which will handle the login process, user input, and API interaction.

    package in.infovistar.expensemanager.activities.login;

    import androidx.appcompat.app.AppCompatActivity;

    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;

    import com.google.android.material.textfield.TextInputLayout;

    import java.util.HashMap;

    import in.infovistar.expensemanager.ExpenseManagerApp;
    import in.infovistar.expensemanager.R;
    import in.infovistar.expensemanager.activities.MainActivity;
    import in.infovistar.expensemanager.api.ApiService;
    import in.infovistar.expensemanager.models.LoginModel;
    import in.infovistar.expensemanager.session.Preferences;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;

    public class LoginActivity extends AppCompatActivity {

    private ExpenseManagerApp expenseManagerApp;
    private Context context;
    private Preferences preferences;
    private ApiService apiService;

    private TextInputLayout tilEmail;
    private TextInputLayout tilPassword;
    private TextView btnLogin;

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

    init();

    btnLogin.setOnClickListener(v -> {
    validate();
    });
    }

    private void validate() {
    if(tilEmail.getEditText().getText().toString().trim().isEmpty()) {
    tilEmail.setError(getString(R.string.please_enter_email_address));
    } else {
    tilEmail.setErrorEnabled(false);
    if(tilPassword.getEditText().getText().toString().trim().isEmpty()) {
    tilPassword.setError(getString(R.string.please_enter_password));
    } else {
    tilPassword.setErrorEnabled(false);
    submit();
    }
    }
    }

    private void submit() {
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("email", tilEmail.getEditText().getText().toString());
    hashMap.put("password", tilPassword.getEditText().getText().toString());
    apiService
    .getLogin(hashMap)
    .enqueue(new Callback<LoginModel>() {
    @Override
    public void onResponse(Call<LoginModel> call, Response<LoginModel> response) {
    if(response.isSuccessful()) {
    Toast.makeText(context, response.body().getMessage(), Toast.LENGTH_SHORT).show();
    if(response.body().getStatus()) {
    preferences.setLogin(response.body().getResult());
    startActivity(new Intent(context, MainActivity.class));
    finish();
    }
    }
    }

    @Override
    public void onFailure(Call<LoginModel> call, Throwable t) {
    Toast.makeText(expenseManagerApp, t.getMessage(), Toast.LENGTH_SHORT).show();
    }
    });
    }

    private void init() {
    context = this;
    expenseManagerApp = (ExpenseManagerApp) getApplicationContext();
    preferences = expenseManagerApp.getPreferences();
    apiService = expenseManagerApp.getApiService();

    tilEmail = findViewById(R.id.til_email);
    tilPassword = findViewById(R.id.til_password);
    btnLogin = findViewById(R.id.btn_login);

    }
    }

     


    5. Session Management

    Use the Session class defined earlier to manage user login status and store the user token for authenticated requests.

     


    6. API Endpoint Example

    Ensure that the API endpoint (/users/login) is correctly implemented on your server side. The server should validate the credentials and return a response with a success status, a message, and a token if authentication is successful.

     


    8. Testing and Integration

    • Testing: Run the app on an emulator or physical device. Enter valid and invalid credentials to test the response.
    • Error Handling: Implement robust error handling to manage network errors, validation errors, and unexpected issues.

     

    Login | Expense Manager Android App

     

    Let me know if you need further assistance or if you need to make any adjustments to the code!