Android Kotlin Project – Cab Booking Application

FREE Online Courses: Dive into Knowledge for Free. Learn More!

Welcome! In this project, we will talk about the essential steps required to develop a cab booking application in Android Studio using Firebase Console and Google API.

Here, we will be talking about topics like integrating Google Maps API to provide real-time location tracking and route navigation implementing user authentication to ensure secure and personalised experiences and much more. Let’s get started right away!

About Android Kotlin Cab Booking Application

Online Cab Booking has somehow become one of the pillar stones of metropolitan cities. Ever since services like Uber, and OLA have entered the online ride-hailing space in India, hitching a ride from one place to another has never been this easy. The service is quite simple. A person wanting to travel from one place to another uses the application and sets their current location and the location that they want to travel to. If a driver accepts the order, they have to take the customer to their location. Payment is set accordingly.

Flow of Application:

The cab booking app follows a user-friendly flow that simplifies the process of booking a cab for travelling purposes.

Here’s a breakdown of its main features and the flow of the app:

  • User Registration: Users who wish to travel and users who want to be drivers can easily register through the app. They will have the option to specify their name, phone number, birth date, gender, and, for drivers, their vehicle type and vehicle number.
  • Login: Users can log in to the app using the credentials that they previously provided in the registration. This makes sure that only authorised individuals are allowed to access the application.
  • Home Screen: Depending on the type of user(customer or driver), users are forwarded to different home screens. Customers have the option to choose a ride, and drivers have the option to accept a ride.
  • Booking a Cab: The customer is allowed to select the type of ride they want to take. The user has to set their current location and the location they want to go to. Then the application provides an estimate of the fare for the ride. This fare will depend on the distance covered and the type of ride taken by the customer.
  • Driver Confirmation: Using real time data and depending upon the user’s location, drivers are given the option to either accept or reject the booking order. If the driver accepts, the customer is informed about the driver and their details.
  • Real-time Tracking: Once the driver confirms the booking, the user can track the location of the cab on a map, and similarly, the driver can track the location of the pickup and drop. Users can estimate the cab’s arrival time and stay informed with the help of this feature.
  • Ride Completion: The ride can be marked as completed in the app once the user arrives at their destination. The trip’s fare is determined by taking into account variables like time, distance, and any supplemental costs.
  • Rate and Feedback: The user has the option to rate and provide feedback on the driver and the overall experience after the trip. This keeps service quality high and aids in the decision-making of other users.

Requirements for the Android Kotlin Cab Booking Project:

To develop an Android Kotlin project, you’ll need the following requirements:

  • Knowledge of Java or Kotlin
  • Android Studio.
  • Understanding of Firebase Hosting and Database.
  • Understanding of Google API(specifically Google Maps API)
  • Knowledge of XML Layout Design.
  • User Interface Design Concepts
  • Object Oriented Programming Concepts

By fulfilling these requirements, you’ll be well-equipped to develop a cab booking project.

Android Kotlin Cab Booking Project Description:

On downloading the code, you are going to see many folders. This section gives you a brief about which folders and files are essential for your project:

1. Manifest File (AndroidManifest.xml): The manifest file gives the Android system crucial details about your app, including the package name, activities, services, permissions, and hardware requirements.

2. Java Class Code (com.example.cabbooking) : There are multiple classes in our project. Dividing our whole project into these multiple classes offers advantages such as modularity, code organisation, reusability, scalability, maintainability, and improved readability and debugging. The whole code is divided and separated into the following folders:

  • Model Classes: Mainly contains the main basic models for the driver, booking and user profile.
  • ui Classes: The UI for the driver, customer and user profiles is given here.
  • Activities Classes: The UI and activities for the login, main menu, user registration, splash activity and its following subclasses are given here.

3. Resources:

  • drawable: Contains the icons/vectors required for the application.
  • Font: Contains the external additional font details.
  • Layout: Contains the basic designs of the different pages of the application.
  • Values: Contains the references to different values used in the application.
  • Menu: Contains the layout of the search menu option.
  • xml: Contains the rules for the data abstraction to be used for the Firebase database.

4. Gradle Scripts: This contains all the dependencies and libraries that we will import into our project.

Download Android Kotlin Cab Booking Application.

Please download the source code of Android Kotlin Cab Booking Application: Android Kotlin Cab Booking Application Project Code.

Steps to Implement the Android Kotlin Cab Booking Application:

1. Download the source code.
2. Locate the zip file and unzip it.
3. Open Android Studio and click on “Open an Existing Project”.
4. Locate and select the source code folder.
5. The IDE should look like this:

cab booking app

6. On opening the folder in Android Studio, make sure the Gradle scripts and the settings are suitable for your IDE and SDK.
7. Work around the files and try to figure out what they all do. Start with AndroidManifest.xml and follow the different codes to different ends.

Main Activity:

This is the front page file of our application. ImageView and TextView is the place holder for our images and texts, and there are certain buttons that, on click, go to their specific pages.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.java:
package com.example.cabbooking;

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.example.cabbooking.R;
import com.example.cabbooking.model.User;
import com.example.cabbooking.ui.customer.booking.BookingViewModel;
import com.example.cabbooking.ui.customer.booking.checkout.CheckoutViewModel;
import com.example.cabbooking.ui.customer.booking.dropoff.DropoffViewModel;
import com.example.cabbooking.ui.customer.booking.pickup.PickupViewModel;
import com.example.cabbooking.ui.customer.home.CustomerHomeViewModel;
import com.example.cabbooking.ui.driver.home.DriverHomeViewModel;
import com.example.cabbooking.ui.user_profile.UserProfileViewModel;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.material.navigation.NavigationView;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;

public class MainActivity extends AppCompatActivity {

    private AppBarConfiguration mAppBarConfiguration;

    private TextView navHeaderEmailTextView;
    private TextView navHeaderUsernameTextView;

    //Firebase, FireStore
    private FirebaseAuth mAuth;
    private FirebaseFirestore db;
    private FirebaseUser currentUser;

    //Current user info
    User currentUserObject = null;

    //View models
    CustomerHomeViewModel customerHomeViewModel;
    DriverHomeViewModel driverHomeViewModel;
    DropoffViewModel dropoffViewModel;
    PickupViewModel pickupViewModel;
    BookingViewModel bookingViewModel;
    CheckoutViewModel checkoutViewModel;
    UserProfileViewModel userProfileViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); //setup navigation drawer
        linkViewElements(); //Get view elements
        initAllChildFragmentsViewModel(); //Init all child fragments viewModels
        initFirebaseCurrentUserInfo(); //Get all fireStore instances
    }

    /**
     * Connect view elements of layout to this class variable
     */
    private void linkViewElements() {
        NavigationView navigationView = findViewById(R.id.nav_view);
        LinearLayout navHeaderView = (LinearLayout) navigationView.getHeaderView(0);
        navHeaderUsernameTextView = (TextView) navHeaderView.getChildAt(1);
        navHeaderEmailTextView = (TextView) navHeaderView.getChildAt(2);
    }

    /**
     * Set up navigation drawer activity
     */
    private void navigationDrawerSetup() {
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);

        mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.nav_customer_home,
                R.id.nav_driver_home,
                R.id.nav_profile)
                .setOpenableLayout(drawer)
                .build();

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);

        navigateAndHideAccordingMenuBasedOnRole(navController);
    }

    /**
     * Logout menu item listener (sits in 3-dots collapsing menu)
     */
    private void onLogoutOptionClick() {
        mAuth.signOut();
        Intent i = new Intent(MainActivity.this, StartActivity.class);
        startActivity(i);
        finish();
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_logout:
                onLogoutOptionClick();
                break;
            default:
                return super.onOptionsItemSelected(item);
        }
        return true;
    }

    /**
     * Get instances of Firebase FireStore Auth, db, current user
     */
    private void initFirebaseCurrentUserInfo() {
        //Get instances of Firebase FireStore
        mAuth = FirebaseAuth.getInstance();
        db = FirebaseFirestore.getInstance();
        currentUser = mAuth.getCurrentUser();
        getCurrentUserObject(); //Get current user object info
    }

    /**
     * Get current user object from FireStore
     */
    private void getCurrentUserObject() {
        db.collection(Constants.FSUser.userCollection)
                .whereEqualTo(Constants.FSUser.emailField, currentUser.getEmail())
                .get()
                .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                    @Override
                    public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                        for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
                            currentUserObject = doc.toObject(User.class);
                            setNavHeaderEmailAndUsername(); //Set nav header username and email
                            setAllChildFragmentsViewModelData();
                            navigationDrawerSetup();
                        }
                    }
                });
    }

    /**
     * navigate to the right home and remove according home menu based on user role
     * @param navController navController object of this layout
     */
    private void navigateAndHideAccordingMenuBasedOnRole(NavController navController){
        NavigationView navigationView = findViewById(R.id.nav_view);
        Menu menu = navigationView.getMenu();

        //Hide according menu and Navigate to the right fragment based on
        if (currentUserObject.getRole().equals("Customer")){
            MenuItem driverHomeMenuItem = menu.getItem(1);
            driverHomeMenuItem.setVisible(false);
            navController.navigate(R.id.nav_customer_home);
        } else {
            MenuItem customerHomeMenuItem = menu.getItem(0);
            customerHomeMenuItem.setVisible(false);
            navController.navigate(R.id.nav_driver_home);
        }
    }

    /**
     * Init all child fragments' view models
     */
    private void initAllChildFragmentsViewModel() {
        customerHomeViewModel = new ViewModelProvider(this).get(CustomerHomeViewModel.class);
        driverHomeViewModel = new ViewModelProvider(this).get(DriverHomeViewModel.class);
        dropoffViewModel = new ViewModelProvider(this).get(DropoffViewModel.class);
        pickupViewModel = new ViewModelProvider(this).get(PickupViewModel.class);
        bookingViewModel = new ViewModelProvider(this).get(BookingViewModel.class);
        checkoutViewModel = new ViewModelProvider(this).get(CheckoutViewModel.class);
        userProfileViewModel = new ViewModelProvider(this).get(UserProfileViewModel.class);
    }

    /**
     * Set nav header username and email
     */
    private void setNavHeaderEmailAndUsername() {
        navHeaderEmailTextView.setText(currentUser.getEmail());
        navHeaderUsernameTextView.setText(currentUserObject.getUsername());
    }

    /**
     * Send current user data through child fragments' view models
     */
    private void setAllChildFragmentsViewModelData() {
        if (currentUserObject.getRole().equals("Customer")){
            customerHomeViewModel.setCurrentUserObject(currentUserObject);
        } else {
            driverHomeViewModel.setCurrentUserObject(currentUserObject);
        }
        dropoffViewModel.setCurrentUserObject(currentUserObject);
        pickupViewModel.setCurrentUserObject(currentUserObject);
        bookingViewModel.setCurrentUserObject(currentUserObject);
        userProfileViewModel.setCurrentUserObject(currentUserObject);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp();
    }
}

Main Activity

Login Activity:

activity_login.xml: This file contains the basic layout of the login activity that we are going to use.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <View
        android:id="@+id/topView"
        android:layout_width="0dp"
        android:layout_height="150dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view2"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="@drawable/wave"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/topView" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_person" />

    <TextView
        android:id="@+id/topText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="48dp"
        android:text="Already have an account ?"
        android:textColor="@color/white"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toStartOf="@+id/imageView"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/topView"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/loginEmailEditText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginEnd="32dp"
        android:background="@drawable/round_border"
        android:drawableStart="@drawable/ic_email"
        android:drawablePadding="16dp"
        android:ems="10"
        android:hint="Email"
        android:inputType="text"
        android:padding="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view2" />

    <EditText
        android:id="@+id/loginPasswordEditText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:background="@drawable/round_border"
        android:drawableStart="@drawable/ic_lock"
        android:drawablePadding="16dp"
        android:ems="10"
        android:hint="Password"
        android:inputType="textPassword"
        android:padding="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/loginEmailEditText" />

    <Button
        android:id="@+id/loginLoginBtn"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:background="@drawable/round_background"
        android:text="Login"
        android:textColor="@color/white"
        android:textStyle="bold"
        app:backgroundTint="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/loginPasswordEditText" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:text="FORGOT PASSWORD"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/loginLoginBtn" />

    <TextView
        android:id="@+id/text_paymentMethods"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:text="OR"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <TextView
        android:id="@+id/moveToRegisterTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:text="New User? Register Now "
        android:textColor="@color/black"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_paymentMethods" />


</androidx.constraintlayout.widget.ConstraintLayout>

LoginActivity.java:

package com.example.cabbooking;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.example.cabbooking.R;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;

public class LoginActivity extends AppCompatActivity {
    Button backBtn, loginBtn;
    EditText emailEditText, passwordEditText;
    TextView moveToRegister;
    private FirebaseAuth mAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        mAuth = FirebaseAuth.getInstance();
        linkViewElements();
        setRegisterTextViewAction();
        setLoginBtnAction();
    }

    //Get View variables from xml id
    private void linkViewElements() {
        loginBtn = findViewById(R.id.loginLoginBtn);
        emailEditText = findViewById(R.id.loginEmailEditText);
        passwordEditText = findViewById(R.id.loginPasswordEditText);
        moveToRegister = findViewById(R.id.moveToRegisterTextView);
    }

    //Login process when clicking 'login' button
    private void setLoginBtnAction() {
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String email = emailEditText.getText().toString();
                final String password = passwordEditText.getText().toString();

                //Check if the input email or password is empty
                if (email.isEmpty() || password.isEmpty()) {
                    Toast.makeText(LoginActivity.this, Constants.ToastMessage.emptyInputError,
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                //Call FirebaseAuth for authentication process
                mAuth.signInWithEmailAndPassword(email, password)
                        .addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
                            @Override
                            public void onComplete(@NonNull Task<AuthResult> task) {
                                if (task.isSuccessful()) {
                                    Toast.makeText(LoginActivity.this, Constants.ToastMessage.signInSuccess,
                                            Toast.LENGTH_SHORT).show();
                                    moveToHomePage(); //Move to HomeActivity
                                } else {
                                    Toast.makeText(LoginActivity.this, Constants.ToastMessage.signInFailure,
                                            Toast.LENGTH_SHORT).show();
                                }

                            }
                        });
            }
        });
    }

    //Move to user's homepage if successfully logged in
    private void moveToHomePage() {
        Intent i = new Intent(LoginActivity.this, MainActivity.class);
        i.putExtra("email", emailEditText.getText().toString());
        startActivity(i);
        finish();
    }

    private void setRegisterTextViewAction() {
        moveToRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(LoginActivity.this, RegisterActivity.class);
                startActivity(i);
                finish();
            }
        });
    }

}

Login Activity

Register Activity:

1. activity_register.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="#EBFAFE"
    tools:context=".RegisterActivity">

    <Button
        android:id="@+id/registerBackBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:backgroundTint="#2AC7FE"
        android:text="Back"
        android:textColor="#000000"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.408">

        <EditText
            android:id="@+id/registerUsernameEditText"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="20sp"
            android:background="#FFFFFF"
            android:ems="10"
            android:hint="Username"
            android:padding="10dp" />

        <EditText
            android:id="@+id/registerPhoneEditText"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="20sp"
            android:background="#FFFFFF"
            android:ems="10"
            android:hint="Phone number"
            android:inputType="phone"
            android:padding="10sp" />


        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginTop="20sp"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/registerBirthEditText"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginRight="10sp"
                android:background="#FFFFFF"
                android:ems="10"
                android:enabled="true"
                android:hint="Birthdate"
                android:inputType="date"
                android:padding="10sp"

                />

            <Button
                android:id="@+id/registerPickdateBtn"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:backgroundTint="#000000"
                android:text="Pick date"
                android:textSize="12sp" />
        </LinearLayout>

        <RadioGroup
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:padding="10sp">

            <RadioButton
                android:id="@+id/registerMaleRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:checked="true"
                android:text="Male" />

            <RadioButton
                android:id="@+id/registerFemaleRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:text="Female" />
        </RadioGroup>

        <RadioGroup
            android:id="@+id/roleGroup"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:padding="10sp">

            <RadioButton
                android:id="@+id/registerCustomerRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:checked="true"
                android:text="Customer" />

            <RadioButton
                android:id="@+id/registerDriverRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:text="Driver" />
        </RadioGroup>

        <RadioGroup
            android:id="@+id/transportationTypeGroup"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:visibility="invisible">

            <RadioButton
                android:id="@+id/registerCarRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:checked="true"
                android:text="car" />

            <RadioButton
                android:id="@+id/registerBikeRadioBtn"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:text="bike" />
        </RadioGroup>

        <EditText
            android:id="@+id/registerVehiclePlateNumberEditText"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginRight="10sp"
            android:background="#FFFFFF"
            android:ems="10"
            android:enabled="true"
            android:hint="Vehicle plate number"
            android:padding="10sp"
            android:visibility="invisible" />

        <Button
            android:id="@+id/registerFinalRegisterBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/black"
            android:backgroundTint="#000000"
            android:padding="10dp"
            android:text="Next"
            android:textColor="#FFFFFF"
            android:textSize="18sp"
            app:backgroundTint="@color/black" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
RegisterActivity.java:
package com.example.cabbooking;


import android.annotation.SuppressLint;
import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.cabbooking.R;

import java.util.Calendar;

public class RegisterActivity extends AppCompatActivity {
    EditText birthDateEditText, usernameEditText, phoneEditText, registerVehiclePlateNumberEditText;
    Button backBtn, nextBtn, datePickerBtn;
    RadioButton maleRadioBtn, femaleRadioBtn;
    RadioGroup roleGroup;
    RadioButton driverRadioBtn, customerRadioBtn;
    RadioGroup transportationTypeGroup;
    RadioButton registerCarRadioBtn, registerBikeRadioBtn;
    private int year, month, day;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);
        linkViewElements();
        setRoleGroupBtnActionHandler();
        setBackBtnAction();
        setDatePickerBtnAction();
        setNextBtnAction();
        setBirthDateEditTextAutoFormat();
    }

    //Get View variables from xml id
    private void linkViewElements() {
        birthDateEditText = (EditText) findViewById(R.id.registerBirthEditText);
        usernameEditText = (EditText) findViewById(R.id.registerUsernameEditText);
        phoneEditText = (EditText) findViewById(R.id.registerPhoneEditText);
        backBtn = (Button) findViewById(R.id.registerBackBtn);
        nextBtn = (Button) findViewById(R.id.registerFinalRegisterBtn);
        datePickerBtn = (Button) findViewById(R.id.registerPickdateBtn);
        maleRadioBtn = (RadioButton) findViewById(R.id.registerMaleRadioBtn);
        femaleRadioBtn = (RadioButton) findViewById(R.id.registerFemaleRadioBtn);
        roleGroup = (RadioGroup) findViewById(R.id.roleGroup);
        customerRadioBtn = (RadioButton) findViewById(R.id.registerCustomerRadioBtn);
        driverRadioBtn = (RadioButton) findViewById(R.id.registerDriverRadioBtn);
        transportationTypeGroup = (RadioGroup) findViewById(R.id.transportationTypeGroup);
        registerCarRadioBtn = (RadioButton) findViewById(R.id.registerCarRadioBtn);
        registerBikeRadioBtn = (RadioButton) findViewById(R.id.registerBikeRadioBtn);
        registerVehiclePlateNumberEditText = (EditText) findViewById(R.id.registerVehiclePlateNumberEditText);
    }

    private void setRoleGroupBtnActionHandler() {
        roleGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @SuppressLint("NonConstantResourceId")
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch(checkedId)
                {
                    case R.id.registerCustomerRadioBtn:
                        transportationTypeGroup.setVisibility(View.INVISIBLE);
                        registerVehiclePlateNumberEditText.setVisibility(View.INVISIBLE);
                        break;
                    case R.id.registerDriverRadioBtn:
                        transportationTypeGroup.setVisibility(View.VISIBLE);
                        registerVehiclePlateNumberEditText.setVisibility(View.VISIBLE);
                        break;

                }
            }
        });
    }

    
    //Move back to startActivity when pressing 'back' button
    private void setBackBtnAction() {
        backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(RegisterActivity.this, StartActivity.class);
                startActivity(i);
                finish();
            }
        });
    }

    //Move to RegisterFinalActivity when pressing 'next', also passing inputted data of user
    private void setNextBtnAction() {
        nextBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = usernameEditText.getText().toString();
                String phone = phoneEditText.getText().toString();
                String birthDate = birthDateEditText.getText().toString();
                String gender = maleRadioBtn.isChecked() ? "Male" : "Female";
                String role = customerRadioBtn.isChecked() ? "Customer" : "Driver";
                String transportationType = "";
                if (driverRadioBtn.isChecked()) {
                    transportationType = registerCarRadioBtn.isChecked() ? "car" : "bike";
                }
                String vehiclePlateNumber = registerVehiclePlateNumberEditText.getText().toString();

                //Check empty input
                if (checkEmptyInput(username, phone, birthDate)) {
                    Toast.makeText(RegisterActivity.this, Constants.ToastMessage.emptyInputError, Toast.LENGTH_SHORT).show();
                } else {
                    //Transfer data
                    Intent i = new Intent(RegisterActivity.this, RegisterFinalActivity.class);
                    i.putExtra(Constants.FSUser.usernameField, username);
                    i.putExtra(Constants.FSUser.phoneField, phone);
                    i.putExtra(Constants.FSUser.birthDateField, birthDate);
                    i.putExtra(Constants.FSUser.genderField, gender);
                    i.putExtra(Constants.FSUser.roleField, role);
                    i.putExtra(Constants.FSUser.transportationType, transportationType);
                    i.putExtra(Constants.FSUser.vehiclePlateNumber, vehiclePlateNumber);
                    startActivity(i);
                    finish();
                }
            }
        });
    }

    //Check if one of the input is empty
    private boolean checkEmptyInput(String username, String phone, String birthDate) {
        return username.isEmpty() || phone.isEmpty() || birthDate.length() < 9
                || birthDate.contains("D") || birthDate.contains("M") || birthDate.contains("Y");
    }

    //date picker dialog for birthday
    private void setDatePickerBtnAction() {
        final Calendar c = Calendar.getInstance();
        year = c.get(Calendar.YEAR);
        month = c.get(Calendar.MONTH);
        day = c.get(Calendar.DAY_OF_MONTH);

        datePickerBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DatePickerDialog datePickerDialog = new DatePickerDialog(datePickerBtn.getContext(),
                        new DatePickerDialog.OnDateSetListener() {
                            @SuppressLint("SetTextI18n")
                            @Override
                            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                                birthDateEditText.setText(dayOfMonth + "/" + (monthOfYear + 1) + "/" + year);
                            }
                        }, year, month, day);
                datePickerDialog.show();
            }
        });

    }

    //Validation after input birth date in the edit text
    private void setBirthDateEditTextAutoFormat() {

        birthDateEditText.addTextChangedListener(new TextWatcher() {
            private String curDateStr = "";
            private final Calendar calendar = Calendar.getInstance();
            private final int tempYear = calendar.get(Calendar.YEAR);

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @SuppressLint("DefaultLocale")
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                //Take action at most 1 number is changed at a time.
                if (!s.toString().equals(curDateStr) && count == 1) {
                    //Current date string in the edit text, after latest change, without the "/" character
                    String curDateStrAfterChangedWithoutSlash = s.toString().replaceAll("[^\\d.]|\\.", "");
                    //Current date string in the edit text, before the latest change, without the "/" character
                    String curDateStrBeforeChangedWithoutSlash = curDateStr.replaceAll("[^\\d.]|\\.", "");

                    int dateStrAfterChangedLen = curDateStrAfterChangedWithoutSlash.length();
                    int cursorPos = dateStrAfterChangedLen; //Cursor position

                    for (int i = 2; i <= dateStrAfterChangedLen && i < 6; i += 2) {
                        cursorPos++;
                    }

                    //If delete the slash character "/", move cursor back 1 position
                    if (curDateStrAfterChangedWithoutSlash.equals(curDateStrBeforeChangedWithoutSlash))
                        cursorPos--;

                    //If the current date string, after latest change, without slash, is not fully filled
                    if (curDateStrAfterChangedWithoutSlash.length() < 8) {
                        String dateFormat = "DDMMYYYY";
                        //
                        curDateStrAfterChangedWithoutSlash = curDateStrAfterChangedWithoutSlash
                                + dateFormat.substring(curDateStrAfterChangedWithoutSlash.length());
                    } else {
                        //Validate and fix the input date if necessary
                        int day = Integer.parseInt(curDateStrAfterChangedWithoutSlash.substring(0, 2));
                        int month = Integer.parseInt(curDateStrAfterChangedWithoutSlash.substring(2, 4));
                        int year = Integer.parseInt(curDateStrAfterChangedWithoutSlash.substring(4, 8));

                        month = month < 1 ? 1 : Math.min(month, 12); //Max month is 12
                        calendar.set(Calendar.MONTH, month - 1);

                        year = (year < 1900) ? 1900 : Math.min(year, tempYear); //Max year for birthday is this year
                        calendar.set(Calendar.YEAR, year);

                        //Get the right day according to the input year and month
                        day = Math.min(day, calendar.getActualMaximum(Calendar.DATE));
                        curDateStrAfterChangedWithoutSlash = String.format("%02d%02d%02d", day, month, year);
                    }

                    //finalize the form of displayed date string
                    curDateStrAfterChangedWithoutSlash = String.format("%s/%s/%s", curDateStrAfterChangedWithoutSlash.substring(0, 2),
                            curDateStrAfterChangedWithoutSlash.substring(2, 4),
                            curDateStrAfterChangedWithoutSlash.substring(4, 8));

                    //Set date string as text in the EditText view and set the cursor position, update current date string
                    cursorPos = Math.max(cursorPos, 0);
                    curDateStr = curDateStrAfterChangedWithoutSlash;
                    birthDateEditText.setText(curDateStr);
                    birthDateEditText.setSelection(Math.min(cursorPos, curDateStr.length()));
                }
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }


}

activity register

8. Press the run button at the top bar. It will install the application on a virtual device.
9. Test if the application runs correctly.
10. Customise to your taste.

Android Kotlin Cab Booking Application Output

The application for a customer should look like this, and the navigation of the application should follow the given path:

Cab Booking Application output

Cab Booking Application

output Cab Booking Application

The application for a driver should look like this, and the navigation of the application should follow the given path:

driver information

 

Cab Booking App output

Summary

This guide gives a detailed explanation for implementing the Cab Booking app using Android Studio. By leveraging multiple classes, users can register as drivers or customers. Then, using Fragments in Java the different UI components can be managed separately, which makes the changing of the UI for the application easier. Thank you for reading!

Leave a Reply

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