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:
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(); } }
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(); } }); } }
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) { } }); } }
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:
The application for a driver should look like this, and the navigation of the application should follow the given path:
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!