Enter the attendance timings using Room Database and Huawei Analytics Kit in Attendance Tracker Android app (Kotlin) – Part 5
Tags: kotlin, analytics, android, xml
Published: 2022-03-29 06:29:38


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkr43cl19105c3prm3mp.png)
**Introduction**

In this article, we can learn about that how to enter In Time and Out time of an users by the Room Database and also we can integrate the Analytics Kit for event tracking.

Room is an **ORM** (Object Relational Mapping) library. In other words, Room will map our database objects to Java objects. Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room can provide to create sqlite databases and perform the operations like create, read, update and delete.

So, I will provide the series of articles on this Attendance Tracker App, in upcoming articles I will integrate other Huawei Kits.

If you are new to this application, follow my previous articles

[Beginner: Integration of Huawei Account Kit of Obtaining Icon Resources feature in Attendance Tracker Android app (Kotlin) - Part 1](https://forums.developer.huawei.com/forumPortal/en/topic/0201810952098220013?fid=0101187876626530001)

[Beginner: Find Log in via SMS and Forgot password? Features using Huawei Account Kit, and Ads Kit in Attendance Tracker Android app (Kotlin) – Part 2](https://forums.developer.huawei.com/forumPortal/en/topic/0201817532838860004?fid=0101187876626530001)

[Beginner: Find the CRUD operations in Room Database in Attendance Tracker Android app (Kotlin) – Part 3](https://forums.developer.huawei.com/forumPortal/en/topic/0202827964929470059)

[Beginner: Integration of Huawei Crash Service in Attendance Tracker Android app (Kotlin) – Part 4](https://forums.developer.huawei.com/forumPortal/en/topic/0202829038661510069)

**Analytics Kit**

**HUAWEI Analytics Kit** provides analysis models to understand user behaviour and gain in-depth insights into **users**, **products** and **content**. It helps you to gain insight about user behaviour on different platforms based on the user behaviour events and user attributes reported by through apps.

**AppGallery Connect**

Find the Analytics using AppGallery connect dashboard.

Choose **My Projects > Huawei Analytics > Overview > Project overview**.

Project overview displays the core indicators of current project, such as the number of **new users, User activity, User acquisition, User revisit, New user retention, Active user retention, User characteristics** and **Popular pages** etc. providing a quick overview of the users and how they are using your app.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7fxsqsgpxbrq1iw42im.PNG)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i24kwmk35b7hhblduhba.PNG)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wlk4pdmudevwpsdq18yp.PNG)

**Requirements**

1. Any operating system (MacOS, Linux and Windows).
2. Must have a Huawei phone with HMS 4.0.0.300 or later.
3. Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 and above installed.
4. Minimum API Level 24 is required.
5. Required EMUI 9.0.0 and later version devices.

**How to integrate HMS Dependencies**

- First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a [Huawei ID](https://developer.huawei.com/consumer/en/doc/start/registration-and-verification-0000001053628148).
- Create a project in android studio, refer [Creating an Android Studio Project](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#1).
- Generate a **SHA-256 certificate fingerprint**.
- To generate **SHA-256 certificate fingerprint**. On right-upper corner of android project click **Gradle**, choose **Project Name > Tasks > android**, and then click **signingReport**, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nkwecc0qqgh4pjt5zki.png)

Note: Project Name depends on the user created name.
- [Create an App in AppGallery Connect](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0).
- Download the **agconnect-services.json** file from **App information**, copy and paste in android **Project** under **app** directory, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhztrfjp1a3uyo0z3dhz.png)

- Enter **SHA-256 certificate fingerprint** and click **Save** button, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qwz5xoftbufxhoerrf3.png)

- Click **Manage APIs** tab and enable **HUAWEI Analytics**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75qzg5sbl54r18zai9xy.jpg)
- Add the below maven URL in **build.gradle(Project)** file under the repositories of **buildscript, dependencies** and **allprojects**, refer [Add Configuration](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#6).
`maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'`
- Add the below plugin and dependencies in **build.gradle(Module)** file.
`apply plugin: id 'com.huawei.agconnect'
apply plugin: id 'kotlin-kapt'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
// Room Database
implementation "androidx.room:room-runtime:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.room:room-ktx:2.4.2"
androidTestImplementation "androidx.room:room-testing:2.4.2"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
// Recyclerview
implementation 'androidx.recyclerview:recyclerview:1.2.1'
// Huawei Analytics
implementation 'com.huawei.hms:hianalytics:6.4.0.300'`
- Now Sync the gradle.
- Add the required permission to the **AndroidManifest.xml** file.
`<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />`

**Let us move to development**

I have created a project on Android studio with empty activity let us start coding.

Initialize Huawei Analytics and enable it.
`// Initialize the Analytics
var mInstance: HiAnalyticsInstance? = null
// Initialize the Analytics function
initAna()

private fun initAna() {
     // Enable Analytics Kit Log
    HiAnalyticsTools.enableLog()
// Generate the Analytics Instance
    mInstance = HiAnalytics.getInstance(this)
// Enable collection capability
    mInstance?.setAnalyticsEnabled(true)
}`

Create a **PunchRecord.kt** class annotated with @Entity to create a table for each class.
`@Entity(tableName = "punchrecords")
data class PunchRecord(
    @PrimaryKey(autoGenerate = true)
    val id: Long,
    val empid: String,
    val intime: String,
    val outtime: String
)`

Create a **PunchRecordDao.kt** interface class annotated with @Dao and responsible for defining the methods that access the database.
`@Dao
interface PunchRecordDao {
    @Query("SELECT * from punchrecords")
    fun punchgetall(): LiveData<List<PunchRecord>>

    @Insert(onConflict = OnConflictStrategy.ABORT)
    suspend fun insert(item: PunchRecord)

    @Query("SELECT * FROM punchrecords WHERE punchrecords.id == :id")
    fun get(id: Long): LiveData<PunchRecord>

    @Update
    suspend fun update(vararg items: PunchRecord)

    @Delete
    suspend fun delete(vararg items: PunchRecord)
}`

Create a **AppRoomDatabase.kt** abstract class that extends **RoomDatabase** annotated with @Database to lists the entities contained in the database, and the DAOs which access them.
`@Database(entities = [PunchRecord::class], version = 1)
abstract class PunchAppRoomDatabase : RoomDatabase() {

    abstract fun punchrecordDao(): PunchRecordDao

    companion object {
        @Volatile
        private var INSTANCE: RoomDatabase? = null

        fun getDatabase(context: Context): PunchAppRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance as PunchAppRoomDatabase
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(context.applicationContext,PunchAppRoomDatabase::class.java,
                    "datarecords_database").fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                return instance
            }
        }
    }

}`

Create a **PunchRepository.kt** class to find the functions.
`class PunchRepository(private val punchDao: PunchRecordDao) {

    val myallItems: LiveData<List<PunchRecord>> = punchDao.punchgetall()

    fun getrepo(id: Long):LiveData<PunchRecord> {
        return punchDao.get(id)
    }
    suspend fun updatepunch(item: PunchRecord) {
        punchDao.update(item)
    }
    suspend fun insertpunch(item: PunchRecord) {
        punchDao.insert(item)
    }
    suspend fun deletepunch(item: PunchRecord) {
        punchDao.delete(item)
    }
    
}`

Create a **PunchViewModel.kt** class that extends **AndroidViewModel** and provides the **PunchRepository** functions.
`class PunchViewModel(application: Application): AndroidViewModel(application) {

    private val repository: PunchRepository
    val allPunchItems: LiveData<List<PunchRecord>>

    init {
        Log.d(ContentValues.TAG, "Inside ViewModel init")
        val dao = PunchAppRoomDatabase.getDatabase(application).punchrecordDao()
        repository = PunchRepository(dao)
        allPunchItems = repository.myallItems
    }

    fun insert(item: PunchRecord) = viewModelScope.launch {
        repository.insertpunch(item)
    }

    fun update(item: PunchRecord) = viewModelScope.launch {
        repository.updatepunch(item)
    }

    fun delete(item: PunchRecord) = viewModelScope.launch {
        repository.deletepunch(item)
    }

    fun get(id: Long) = repository.getrepo(id)

}`

In the **EmpLoginActivity.kt** activity to find the business logic for button click.
`class EmpLoginActivity : AppCompatActivity() {

    private var buttonAddPunch: Button? = null
    private lateinit var myViewModel: PunchViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_emp_login)

        val recyclerView = recyclerview_list
        val adapter = PunchAdapter(this)
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        buttonAddPunch = findViewById(R.id.btn_punch)
        buttonAddPunch!!.setOnClickListener(View.OnClickListener {
            val intent = Intent(this, TimeActivity::class.java)
            startActivity(intent)
        })

        myViewModel = ViewModelProvider(this)[PunchViewModel::class.java]
        myViewModel.allPunchItems.observe(this, Observer { items ->
            items?.let { adapter.setItems(it) }
        })

    }

}`

Create a **PunchAdapter.kt** adapter class to hold the list.
`class PunchAdapter internal constructor (context: Context) : RecyclerView.Adapter<PunchAdapter.PunchRecordViewHolder>() {

    private val inflater: LayoutInflater = LayoutInflater.from(context)
    private var itemsList = emptyList<PunchRecord>().toMutableList()
    private val onClickListener: View.OnClickListener

    init {
        onClickListener = View.OnClickListener { v ->
            val item = v.tag as PunchRecord
            Log.d(TAG, "Setting onClickListener for item ${item.id}")
            val intent = Intent(v.context, TimeActivity::class.java).apply {
                putExtra(DATA_RECORD_ID, item.id)
            }
            v.context.startActivity(intent)
        }
    }

    inner class PunchRecordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemId: TextView = itemView.findViewById(R.id.datarecord_viewholder_id)
        val itemID: TextView = itemView.findViewById(R.id.punch_id)
        val itemInTime: TextView = itemView.findViewById(R.id.punch_in)
        val itemOutTime: TextView = itemView.findViewById(R.id.punch_out)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PunchRecordViewHolder {
        val itemView = inflater.inflate(R.layout.punch_list, parent, false)
        return PunchRecordViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: PunchRecordViewHolder, position: Int) {
        val current = itemsList[position]
        // Needed: will be referenced in the View.OnClickListener above
        holder.itemView.tag = current
        with(holder) {
            // Set UI values
            itemId.text = current.id.toString()
            // itemRecord.text = current.record
            itemID.text = current.empid
            itemInTime.text = current.intime
            itemOutTime.text = current.outtime
            // Set handlers
            itemView.setOnClickListener(onClickListener)
        }
    }

    internal fun setItems(items: List<PunchRecord>) {
        this.itemsList = items.toMutableList()
        notifyDataSetChanged()
    }

    override fun getItemCount() = itemsList.size

    companion object {
        const val DATA_RECORD_ID : String = "datarecord_id"
    }

}`

In the **TimeActivity.kt** activity to find the business logic to add In Time and Out Time.
`class TimeActivity : AppCompatActivity(), DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {

    private lateinit var dataViewModel: PunchViewModel
    private var recordId: Long = 0L
    private var isEdit: Boolean = false

    var day = 0
    var month = 0
    var year = 0
    var hour = 0
    var minute = 0

    var savedDay = 0
    var savedMonth = 0
    var savedYear = 0
    var savedHour = 0
    var savedMinute = 0

    var isInTime : Boolean = false
    var isOutTime : Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_time)

        dataViewModel = ViewModelProvider(this)[PunchViewModel::class.java]
        if (intent.hasExtra(PunchAdapter.DATA_RECORD_ID)) {
            recordId = intent.getLongExtra(PunchAdapter.DATA_RECORD_ID, 0L)
            dataViewModel.get(recordId).observe(this, Observer {
                val viewId = findViewById<TextView>(R.id.datarecord_id)
                val viewEmpid = findViewById<EditText>(R.id.edtpunch_id)
                val viewInTime = findViewById<EditText>(R.id.edtpunch_intime)
                val viewOutTime = findViewById<EditText>(R.id.edtpunch_outtime)
                if (it != null) {
                    // populate with data
                    viewId.text = it.id.toString()
                    viewEmpid.setText(it.empid)
                    viewInTime.setText(it.intime)
                    viewOutTime.setText(it.outtime)
                }
            })
            isEdit = true
        }

        edtpunch_intime.setOnClickListener{
            isInTime = true
            isOutTime = false
            getDateTimeCalendar()
            DatePickerDialog(this,this,year,month,day ).show()
        }

        edtpunch_outtime.setOnClickListener{
            getDateTimeCalendar()
            isInTime = false
            isOutTime = true
            DatePickerDialog(this,this,year, month, day ).show()
        }

        btnpunch_save.setOnClickListener { view ->
            val id = 0L
            val mEmpid = edtpunch_id.text.toString()
            val mInTime = edtpunch_intime.text.toString()
            val mOutTime = edtpunch_outtime.text.toString()
            if (mEmpid.isBlank() ) {
                Toast.makeText(this, "Employee ID is blank", Toast.LENGTH_SHORT).show()
            }
            else if (mInTime.isBlank()) {
                Toast.makeText(this, "In Time is blank", Toast.LENGTH_SHORT).show()
            }
            else if(mOutTime.isBlank()) {
                Toast.makeText(this, "Out Time is blank", Toast.LENGTH_SHORT).show()
            }
            else {
                val item = PunchRecord(id = id, empid = mEmpid, intime = mInTime, outtime = mOutTime)
                dataViewModel.insert(item)
                finish()
            }
        }
        btnpunch_update.setOnClickListener { view ->
            val id = datarecord_id.text.toString().toLong()
            val nEmpid = edtpunch_id.text.toString()
            val nInTime = edtpunch_intime.text.toString()
            val nOutTime = edtpunch_outtime.text.toString()
            if (nEmpid.isBlank() && nInTime.isBlank() && nOutTime.isBlank()) {
                Toast.makeText(this, "Empty data is not allowed", Toast.LENGTH_SHORT).show()
            } else {
                val item = PunchRecord(id = id, empid = nEmpid, intime = nInTime, outtime = nOutTime)
                dataViewModel.update(item)
                finish()
            }
        }
        btnpunch_delete.setOnClickListener {
            val id = datarecord_id.text.toString().toLong()
            val nEmpid = edtpunch_id.text.toString()
            val nInTime = edtpunch_intime.text.toString()
            val nOutTime = edtpunch_outtime.text.toString()
            val item = PunchRecord( id =id, empid = nEmpid, intime = nInTime, outtime = nOutTime)
            dataViewModel.delete(item)
            finish()
        }

    }

    private fun getDateTimeCalendar(){
        val cal: Calendar = Calendar.getInstance()
        day = cal.get(Calendar.DAY_OF_MONTH)
        month = cal.get(Calendar.MONTH)
        year = cal.get(Calendar.YEAR)
        hour = cal.get(Calendar.HOUR)
        minute = cal.get(Calendar.MINUTE)
    }

    override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
        savedDay = dayOfMonth
        savedMonth = month
        savedYear = year
        getDateTimeCalendar()
        TimePickerDialog(this, this, hour, minute, true).show()
    }
    override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) {
        savedHour = hourOfDay
        savedMinute = minute
        if (isInTime) {
            edtpunch_intime.setText("$savedDay-$savedMonth-$savedYear, $savedHour:$savedMinute")
        }
        if (isOutTime) {
            edtpunch_outtime.setText("$savedDay-$savedMonth-$savedYear, $savedHour:$savedMinute")
        }
    }

}`

In the **activity_emp_login.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".RoomDatabase.EmpLoginActivity">

    <Button
        android:id="@+id/btn_punch"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:textSize="18dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:layout_gravity="center_horizontal"
        android:textAllCaps="false"
        android:text="Click to Punch"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>`

In the **activity_time.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".RoomDatabase.TimeActivity">

    <TextView
        android:id="@+id/datarecord_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="id "
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textSize="18sp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edtpunch_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="EmpID: "
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textSize="18sp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edtpunch_intime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="In Time:  "
        android:textSize="18sp"
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textColor="@color/black" />
    <EditText
        android:id="@+id/edtpunch_outtime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="left"
        android:hint="Out Time:  "
        android:textSize="18sp"
        android:padding="5dp"
        android:layout_marginLeft="10dp"
        android:textColor="@color/black" />
    <Button
        android:id="@+id/btnpunch_save"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Save"/>
    <Button
        android:id="@+id/btnpunch_update"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Update"/>
    <Button
        android:id="@+id/btnpunch_delete"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:textSize="18dp"
        android:textAllCaps="false"
        android:layout_marginTop="5dp"
        android:text="Delete"/>

</LinearLayout>`

In the **punch_list.xml** we can create the UI screen for customized items.
`<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp">
        <TextView
            android:id="@+id/datarecord_viewholder_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="10dp"
            android:textSize="18sp"
            android:text="id: "
            android:textColor="@color/black"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/punch_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:text="ID: " />
        <TextView
            android:id="@+id/punch_in"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:text="IN Time: " />
        <TextView
            android:id="@+id/punch_out"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:text="OUT Time: " />
    </LinearLayout>

</androidx.cardview.widget.CardView>`

**Demo**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/grfd4axggz84gy9m64qe.gif)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vn521xboq786xud4lysm.gif)

**Tips and Tricks**
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to **24** or later, otherwise you will get **AndriodManifest** merge issue.
3. Make sure you have added the **agconnect-services.json** file to app folder.
4. Make sure you have added **SHA-256 fingerprint** without fail.
5. Make sure all the dependencies are added properly.

**Conclusion**
In this article, we can learn about that how to enter In Time and Out time of an users by the Room Database and also, we can integrate the Analytics Kit for event tracking.

I hope you have read this article. If you found it is helpful, please provide likes and comments.

**Reference**
Analytics Kit - [Documentation](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/introduction-0000001050745149)
Analytics Kit – [Training Video](https://developer.huawei.com/consumer/en/training/course/video/101590717176869011)
[Room Database](https://developer.android.com/training/data-storage/room)