Here’s a guide on implementing Customer CRUD (Create, Read, Update, Delete) operations in the Expense Manager Android App using Retrofit. This implementation includes setting up Retrofit to interact with backend APIs to manage customer data, create the necessary UI components, and integrate them with the app.
Customer CRUD Feature for Expense Manager Android App Using Retrofit
Customer CRUD functionality allows you to manage customer data within the Expense Manager app. This involves creating, reading, updating, and deleting customer information, all handled through Retrofit for seamless API communication.
1. Define the ApiService Interface
Define the API endpoints for Customer CRUD operations in your 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 {
// Customer APIs
// Customer List API
@FormUrlEncoded
@POST("customer/list")
Call<CustomerModel> getCustomerList(@FieldMap HashMap<String, Object> hashMap);
// Customer Create API
@FormUrlEncoded
@POST("customer/create")
Call<CustomerModel> createCustomer(@FieldMap HashMap<String, Object> hashMap);
// Customer Details API
@FormUrlEncoded
@POST("customer/details")
Call<CustomerModel> getCustomerDetails(@FieldMap HashMap<String, Object> hashMap);
// Customer Update API
@FormUrlEncoded
@POST("customer/update")
Call<CustomerModel> updateCustomer(@FieldMap HashMap<String, Object> hashMap);
// Customer Delete API
@FormUrlEncoded
@POST("customer/update")
Call<CustomerModel> deleteCustomer(@FieldMap HashMap<String, Object> hashMap);
}
2. Create the Toolbar Layout
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:background="@color/actionbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:gravity="center_vertical"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:textSize="@dimen/font_size_medium"
android:textColor="@color/white"
android:ellipsize="end"
android:gravity="start|center_vertical|center_horizontal|center"
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:maxLines="1"
android:text="@string/app_name"
style="@style/TextViewRegularFont"/>
</LinearLayout>
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
3. Create the Add Customer Screen Layout
activity_add_customer.xml
<?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.customer.AddCustomerActivity">
<include
layout="@layout/toolbar"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/padding_12">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_name"
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/customer_name"
android:textSize="@dimen/font_size_medium"
android:background="@drawable/edittext_bg"
style="@style/EditTextRegularFont"
android:inputType="textPersonName"
android:maxLines="1"/>
<requestFocus/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="3">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_mobile_country"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
android:layout_marginTop="@dimen/margin_16"
android:layout_marginEnd="@dimen/margin_4">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/country_code"
android:textSize="@dimen/font_size_medium"
android:background="@drawable/edittext_bg"
style="@style/EditTextRegularFont"
android:inputType="phone"
android:text="@string/_91"
android:focusable="false"
android:maxLength="10"
android:maxLines="1"/>
<requestFocus/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_mobile_number"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
android:layout_marginTop="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_4">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/mobile_no"
android:textSize="@dimen/font_size_medium"
android:background="@drawable/edittext_bg"
style="@style/EditTextRegularFont"
android:inputType="phone"
android:maxLength="10"
android:maxLines="1"/>
<requestFocus/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_address"
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/address"
android:textSize="@dimen/font_size_medium"
android:background="@drawable/edittext_bg"
style="@style/EditTextRegularFont"
android:inputType="text"
android:maxLines="4"/>
<requestFocus/>
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/tv_add_customer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_12"
android:text="@string/add_customer"
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>
4 . Create Add Customer Activity
Here’s how you can implement the main CRUD operations using separate activities or fragments for the UI. The examples below will focus on the main activity that lists customers and includes buttons to create, update, and delete.
package in.infovistar.expensemanager.activities.customer;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
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.login.LoginActivity;
import in.infovistar.expensemanager.api.ApiService;
import in.infovistar.expensemanager.models.CustomerModel;
import in.infovistar.expensemanager.session.Preferences;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AddCustomerActivity extends AppCompatActivity {
private ExpenseManagerApp expenseManagerApp;
private Context context;
private Preferences preferences;
private ApiService apiService;
private Toolbar toolbar;
private TextView toolbarTitle;
private TextInputLayout tilName;
private TextInputLayout tilMobileCountry;
private TextInputLayout tilMobileNumber;
private TextInputLayout tilAddress;
private TextView tvAddCustomer;
private String type;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_customer);
if(getIntent() != null) {
type = getIntent().getStringExtra("type");
}
init();
tvAddCustomer.setOnClickListener(v -> {
validate();
});
}
private void validate() {
if(tilName.getEditText().getText().toString().trim().isEmpty()) {
tilName.setError(getText(R.string.please_enter_customer_name));
} else {
tilName.setErrorEnabled(false);
if(tilMobileNumber.getEditText().getText().toString().trim().isEmpty()
|| tilMobileNumber.getEditText().getText().toString().trim().length() < 10) {
tilMobileNumber.setError(getText(R.string.please_enter_10_digit_mobile_number));
} else {
tilMobileCountry.setErrorEnabled(false);
submit();
}
}
}
private void submit() {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("type", type);
hashMap.put("name", tilName.getEditText().getText().toString());
hashMap.put("mobile_country", tilMobileCountry.getEditText().getText().toString());
hashMap.put("mobile_number", tilMobileNumber.getEditText().getText().toString());
hashMap.put("address", tilAddress.getEditText().getText().toString());
hashMap.put("status", "1");
apiService
.createCustomer(hashMap)
.enqueue(new Callback<CustomerModel>() {
@Override
public void onResponse(Call<CustomerModel> call, Response<CustomerModel> response) {
if(response.body() != null) {
Toast.makeText(context, response.body().getMessage(), Toast.LENGTH_SHORT).show();
if(response.body().getStatus()) {
finish();
}
}
}
@Override
public void onFailure(Call<CustomerModel> call, Throwable t) {
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void init() {
context = this;
expenseManagerApp = (ExpenseManagerApp) getApplicationContext();
preferences = expenseManagerApp.getPreferences();
apiService = expenseManagerApp.getApiService();
if(preferences.getLogin() == null) {
startActivity(new Intent(context, LoginActivity.class));
finish();
}
toolbar = findViewById(R.id.toolbar);
toolbarTitle = findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
toolbarTitle.setText(getString(R.string.add_customer));
tilName = findViewById(R.id.til_name);
tilMobileCountry = findViewById(R.id.til_mobile_country);
tilMobileNumber = findViewById(R.id.til_mobile_number);
tilAddress = findViewById(R.id.til_address);
tvAddCustomer = findViewById(R.id.tv_add_customer);
}
}
4 . Create Update Customer Activity
package in.infovistar.expensemanager.activities.customer;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
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.login.LoginActivity;
import in.infovistar.expensemanager.api.ApiService;
import in.infovistar.expensemanager.models.CustomerModel;
import in.infovistar.expensemanager.models.CustomerResultModel;
import in.infovistar.expensemanager.session.Preferences;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class UpdateCustomerActivity extends AppCompatActivity {
private ExpenseManagerApp expenseManagerApp;
private Context context;
private Preferences preferences;
private ApiService apiService;
private Toolbar toolbar;
private TextView toolbarTitle;
private TextInputLayout tilName;
private TextInputLayout tilMobileCountry;
private TextInputLayout tilMobileNumber;
private TextInputLayout tilAddress;
private TextView tvAddCustomer;
private String type;
private String customerId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_customer);
if(getIntent() != null) {
type = getIntent().getStringExtra("type");
customerId = getIntent().getStringExtra("customer_id");
}
init();
loadCustomer();
tvAddCustomer.setOnClickListener(v -> {
validate();
});
}
private void loadCustomer() {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("customer_id", customerId);
apiService
.getCustomerDetails(hashMap)
.enqueue(new Callback<CustomerModel>() {
@Override
public void onResponse(Call<CustomerModel> call, Response<CustomerModel> response) {
if(response.body() != null) {
if(response.body().getStatus()) {
populateCustomer(response.body().getResult());
}
}
}
@Override
public void onFailure(Call<CustomerModel> call, Throwable t) {
}
});
}
private void populateCustomer(CustomerResultModel result) {
tilName.getEditText().setText(result.getName());
tilMobileCountry.getEditText().setText(result.getMobileCountry());
tilMobileNumber.getEditText().setText(result.getMobileNumber());
tilAddress.getEditText().setText(result.getAddress());
}
private void validate() {
if(tilName.getEditText().getText().toString().trim().isEmpty()) {
tilName.setError(getText(R.string.please_enter_customer_name));
} else {
tilName.setErrorEnabled(false);
if(tilMobileNumber.getEditText().getText().toString().trim().isEmpty()
|| tilMobileNumber.getEditText().getText().toString().trim().length() < 10) {
tilMobileNumber.setError(getText(R.string.please_enter_10_digit_mobile_number));
} else {
tilMobileCountry.setErrorEnabled(false);
submit();
}
}
}
private void submit() {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("type", type);
hashMap.put("customer_id", customerId);
hashMap.put("name", tilName.getEditText().getText().toString());
hashMap.put("mobile_country", tilMobileCountry.getEditText().getText().toString());
hashMap.put("mobile_number", tilMobileNumber.getEditText().getText().toString());
hashMap.put("address", tilAddress.getEditText().getText().toString());
hashMap.put("status", "1");
apiService
.updateCustomer(hashMap)
.enqueue(new Callback<CustomerModel>() {
@Override
public void onResponse(Call<CustomerModel> call, Response<CustomerModel> response) {
if(response.body() != null) {
Toast.makeText(context, response.body().getMessage(), Toast.LENGTH_SHORT).show();
if(response.body().getStatus()) {
finish();
}
}
}
@Override
public void onFailure(Call<CustomerModel> call, Throwable t) {
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void init() {
context = this;
expenseManagerApp = (ExpenseManagerApp) getApplicationContext();
preferences = expenseManagerApp.getPreferences();
apiService = expenseManagerApp.getApiService();
if(preferences.getLogin() == null) {
startActivity(new Intent(context, LoginActivity.class));
finish();
}
toolbar = findViewById(R.id.toolbar);
toolbarTitle = findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
toolbarTitle.setText(getString(R.string.update_customer));
tilName = findViewById(R.id.til_name);
tilMobileCountry = findViewById(R.id.til_mobile_country);
tilMobileNumber = findViewById(R.id.til_mobile_number);
tilAddress = findViewById(R.id.til_address);
tvAddCustomer = findViewById(R.id.tv_add_customer);
tvAddCustomer.setText(getString(R.string.update_customer));
}
}
5. RecyclerView Adapter for Displaying Customers
package in.infovistar.expensemanager.adapters;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashMap;
import java.util.List;
import in.infovistar.expensemanager.ExpenseManagerApp;
import in.infovistar.expensemanager.R;
import in.infovistar.expensemanager.activities.customer.AddCustomerActivity;
import in.infovistar.expensemanager.activities.customer.UpdateCustomerActivity;
import in.infovistar.expensemanager.api.ApiService;
import in.infovistar.expensemanager.models.CustomerModel;
import in.infovistar.expensemanager.models.CustomerResultModel;
import in.infovistar.expensemanager.session.Preferences;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.ViewHolder> {
private ExpenseManagerApp expenseManagerApp;
private ApiService apiService;
private Context context;
private List<CustomerResultModel> resultModelList;
public CustomerAdapter(List<CustomerResultModel> resultModelList) {
this.resultModelList = resultModelList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_customer, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CustomerResultModel resultModel = resultModelList.get(position);
holder.setCustomerName(resultModel.getName());
holder.setMobileNumber(resultModel.getMobileCountry() + resultModel.getMobileNumber());
}
@Override
public int getItemCount() {
return resultModelList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tvCustomerName;
private TextView tvMobileNumber;
private TextView tvEdit;
private TextView tvDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
context = itemView.getContext();
expenseManagerApp = (ExpenseManagerApp) context.getApplicationContext();
apiService = expenseManagerApp.getApiService();
tvCustomerName = itemView.findViewById(R.id.tv_customer_name);
tvMobileNumber = itemView.findViewById(R.id.tv_mobile_number);
tvEdit = itemView.findViewById(R.id.tv_edit);
tvDelete = itemView.findViewById(R.id.tv_delete);
tvEdit.setOnClickListener(v -> {
CustomerResultModel resultModel = resultModelList.get(getBindingAdapterPosition());
Intent intent = new Intent(context, UpdateCustomerActivity.class);
intent.putExtra("customer_id", resultModel.getId());
intent.putExtra("type", resultModel.getType());
context.startActivity(intent);
});
tvDelete.setOnClickListener(v -> {
showDeleteAlertDialog(getBindingAdapterPosition());
});
}
private void showDeleteAlertDialog(int position) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.delete_message)
.setCancelable(false)
.setPositiveButton(R.string.yes, (dialog, id) -> {
dialog.dismiss();
deleteCustomer(position);
}).setNegativeButton(R.string.no, (dialog, id) -> {
dialog.cancel();
});
AlertDialog alert = builder.create();
alert.show();
}
private void deleteCustomer(int position) {
CustomerResultModel resultModel = resultModelList.get(position);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("customer_id", resultModel.getId());
apiService
.deleteCustomer(hashMap)
.enqueue(new Callback<CustomerModel>() {
@Override
public void onResponse(Call<CustomerModel> call, Response<CustomerModel> response) {
if(response.body() != null) {
Toast.makeText(context, response.body().getMessage(), Toast.LENGTH_SHORT).show();
if(response.body().getStatus()) {
resultModelList.remove(position);
notifyItemRemoved(position);
}
}
}
@Override
public void onFailure(Call<CustomerModel> call, Throwable t) {
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public void setCustomerName(String name) {
tvCustomerName.setText(name);
}
public void setMobileNumber(String mobileNumber) {
tvMobileNumber.setText(mobileNumber);
}
}
}
6. Testing and Integration