Open In App

How to Build Live Train Status App in Android?

Last Updated : 27 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Imagine you are traveling via Indian railways which is one of the biggest rail networks in the world. You want to get real-time updates for the train on which you are traveling so that you can plan accordingly. In this article, we will take a look at How to build a live train status checker application in Android. We will be using a method of web scraping to build this application to check live train status. A sample video is given below to get an idea about what we are going to do in this article.

Step-by-Step Implementation

Step 1 : Create a new android studio project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Add the below dependency in your build.gradle file

Below is the dependency for JSOUP which we will be using to scrap the data for our application. For adding this dependency navigate to the app > Gradle Scripts > build.gradle(app) and add the below dependency in the dependencies section.

implementation ("org.jsoup:jsoup:1.11.2")
implementation ("com.squareup.okhttp3:okhttp:4.9.1")

After adding this dependency sync your project and now move toward the AndroidManifest.xml part.  

Step 3: Adding permissions to the internet in the AndroidManifest.xml file

Navigate to the app > AndroidManifest.xml and add the below code to it. 

XML




<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>


Step 4: Working with the activity_main.xml file

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for activity_main.xml file.

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
      
    <!-- on the below line creating an edit text
          for getting train number -->
    <EditText
        android:id="@+id/idEdtTrainNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="10dp"
        android:hint="Enter Train Number"
        android:inputType="phone"
        android:maxLength="5"
        android:textColor="@color/black"
        android:textSize="18sp" />
  
    <!-- on the below line creating a text view for 
         selecting the date for which we want train status -->
    <TextView
        android:id="@+id/idTVDate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idEdtTrainNumber"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="10dp"
        android:padding="5dp"
        android:text="Select Date"
        android:textColor="@color/black"
        android:textSize="18sp" />
    
    <!-- on the below line creating a progress bar
          for displaying loader -->
    <ProgressBar
        android:id="@+id/idPBLoading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVDate"
        android:visibility="gone" />
    
    <!-- on the below line creating a text view for 
         displaying current train status -->
    <TextView
        android:id="@+id/idTVTrainStatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVDate"
        android:layout_centerHorizontal="true"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="10dp"
        android:padding="10dp"
        android:text=""
        android:textColor="@color/black"
        android:textSize="18sp"
        android:textStyle="bold" />
    
    <!-- on the below line creating a button 
         to get the current train status -->
    <Button
        android:id="@+id/btnView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVTrainStatus"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="25sp"
        android:backgroundTint="#673AB7"
        android:text="Get Live Train Status" />
    
</RelativeLayout>


Step 5: Working with MainActivity.kt file

Navigate to app>java>your app’s package name>MainActivity.kt file. Add below code to it. Comments are added in the code to get to know in detail.

Kotlin




package com.droidcon.kotlinapplication
  
import android.app.DatePickerDialog
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.ProgressBar
  
import okhttp3.OkHttpClient
import okhttp3.Request
  
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
  
class MainActivity : AppCompatActivity() {
  
    // on the below line creating a variable 
      // for train status text view.
    private lateinit var trainStatusTV: TextView
  
    // on the below line creating a variable for 
      // button to get live train status.
    private lateinit var getTrainStatusBtn: Button
  
    // on the below line creating a variable for edit text
      // for entering train number.
    private lateinit var trainNumberEdt: EditText
  
    // on the below line creating a variable for progress bar
      // for displaying loading indicator.
    private lateinit var loadingPB: ProgressBar
  
    // on the below line creating a text view
      // for displaying a date.
    private lateinit var dateTV: TextView
  
    // on the below line creating an integer 
      // variable for year, month as well as day.
    private var mYear = 0
  
    // on the below line creating an integer 
      // variable for year, month as well as day.
    private var mMonth = 0
  
    // on the below line creating an integer 
      // variable for year, month as well as day.
    private var mDay = 0
  
    private var formattedDate = ""
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // on the below line initializing all the variables.
        trainStatusTV = findViewById(R.id.idTVTrainStatus)
        getTrainStatusBtn = findViewById(R.id.btnView)
        trainNumberEdt = findViewById(R.id.idEdtTrainNumber)
        loadingPB = findViewById(R.id.idPBLoading)
        dateTV = findViewById(R.id.idTVDate)
  
        // on the below line adding a click listener for our get train status button.
        // on the below line adding a click listener for our get train status button.
        getTrainStatusBtn.setOnClickListener(View.OnClickListener {
            // on the below line checking if the train number edit text 
              // is not empty as well as date text view is not equal to select date.
            if (!trainNumberEdt.getText().toString().isEmpty() && dateTV.getText()
                    .toString() != "Select Date"
            ) {
                // on the below line we are calling our async task 
                  // to make api call to get live train status.
                checkTrainStatus(trainNumberEdt.getText().toString(), formattedDate)
            } else {
                // on the below line displaying a toast message 
                  // to enter train number as well as date,
                Toast.makeText(
                    this@MainActivity,
                    "Please enter the train number as well as date..",
                    Toast.LENGTH_SHORT
                ).show()
            }
        })
  
        // on the below line adding a click listener for our date text view.
        // on the below line calling a pick date method.
        dateTV.setOnClickListener { 
            pickDate()
        }
    }
  
    // on the below line creating a date picker
      // method to pick the date.
    private fun pickDate() {
  
        // on the below line creating a variable for 
          // calendar and getting instance for the same.
        val c = Calendar.getInstance()
        // on the below line getting current 
          // date with day, month and year.
        mYear = c[Calendar.YEAR]
        mMonth = c[Calendar.MONTH]
        mDay = c[Calendar.DAY_OF_MONTH]
        // on the below line creating and initializing 
          // variable for date picker dialog.
        val datePickerDialog = DatePickerDialog(
            this,
            { view, year, monthOfYear, dayOfMonth -> // on the below line we are getting the date from date picker dialog.
                val dateFormat = dayOfMonth.toString() + "-" + (monthOfYear + 1) + "-" + year
                // on the below line we are specifying the input date
                  // format and output date format.
                val inputDateFormat = SimpleDateFormat("dd-MM-yyyy")
                val outputDateFormat = SimpleDateFormat("yyyyMMdd")
  
                // on the below line parsing input date.
                var inputDate: Date? = null
                try {
                    // on the below line parsing the date.
                    inputDate = inputDateFormat.parse(dateFormat)
                    // Format the parsed date to the desired output format
                    val outputDateStr = outputDateFormat.format(inputDate)
                    // on the below line setting date in a variable.
                    formattedDate = outputDateStr
                } catch (e: Exception) {
                    // on the below line handling exception while parsing the date.
                    e.printStackTrace()
                }
                // on the below line setting date to our text view.
                dateTV.text = dayOfMonth.toString() + "-" + (monthOfYear + 1) + "-" + year
            }, mYear, mMonth, mDay
        )
        // on the below line calling date picker dialog show method to display the dialog.
        datePickerDialog.show()
    }
  
    // on the below line creating a method to check train status. 
    fun checkTrainStatus(trainNumber: String, date: String) {
        // on the below line changing visibility for progress bar. 
        loadingPB.visibility = View.VISIBLE
  
        // on the below line calling global scope. 
        GlobalScope.launch(Dispatchers.IO) {
            // on the below line specifying try and catch block. 
            try {
                // on the below line creating url for making call for web page. 
                val url = "https://runningstatus.in/status/$trainNumber-on-$date"
                // on the below line creating a variable for http client and initializing it. 
                val client = OkHttpClient()
                // on the below line creating and initializing variable for request. 
                val request = Request.Builder().url(url).build()
                // on the below line making a new call. 
                val response = client.newCall(request).execute()
                // on the below line getting the response. 
                val responseBody = response.body?.string()
                // on the below line getting train status by parsing data to inside parse train status method. 
                val trainStatus = parseTrainStatus(responseBody)
  
                // on the below line changing visibility for the progress bar and setting train status to our text view. 
                withContext(Dispatchers.Main) {
                    loadingPB.visibility = View.GONE
                    trainStatusTV.text = trainStatus
                }
                // on the below line handling the exception. 
            } catch (e: Exception) {
                e.printStackTrace()
                // on the below line handling error. 
                withContext(Dispatchers.Main) {
                    loadingPB.visibility = View.GONE
                    trainStatusTV.text = "Please enter a valid train number.."
                }
            }
        }
    }
  
    // on the below line creating a method to parse the response. 
    private fun parseTrainStatus(responseBody: String?): String {
        // on the below line creating and initializing variable for jsoup. 
        val document = Jsoup.parse(responseBody)
        // on the below line getting first element for card header. 
        val cardHeaderElement = document.select("div.card-header").first()
        // on the below line checking if card header element is not null. 
        if (cardHeaderElement != null) {
            // on the below line removing small and a tag. 
            cardHeaderElement.select("small").remove()
            cardHeaderElement.select("a").remove()
            // on the below line returning the text result. 
            return cardHeaderElement.text()
        } else {
            // on the below line returning the error message. 
            return "Please enter a valid train number.."
        }
    }
  
}


Now run your application to see the output of the application.

Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads