Share
Explore

Building the Shopping App : April 12


Here is what we want to build:

image.png

Lecture: Introduction to Android Architecture with Room Database

Objective:

This lecture aims to introduce students to the structure and components of an Android application using Room for data persistence.
The lecture will cover the key classes in the project and explain how they connect with each other and with Android libraries to enable a seamless front-end and back-end interaction.

Overview:

Building modern Android applications requires a solid understanding of application architecture, including how data flows within the application and how it is managed and persisted.
This course will focus on the Room persistence library, which provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

As an exercise for the student: Try making your own diagram of how all these classes connect together:
Creating a textual description of a diagram that outlines the connections between the classes in your Android project can help you visualize the architecture before you draw it or use any diagramming tool.
Here’s a conceptual overview of how you might represent these relationships in a diagram:

Diagram Description:

Application Component:
GroceryListApplication:
Initializes and provides the GroceryDatabase.
Provides GroceryItemDao through the database instance.

Database Component:
GroceryDatabase:
Is a singleton class that provides an instance of the Room database.
Contains the DAO (GroceryItemDao).

Data Access Object (DAO):
GroceryItemDao:
Provides methods for accessing database operations such as insert, delete, update, and query.

ViewModel and Factory:
GroceryViewModelFactory:
Creates instances of GroceryViewModel, injecting the necessary DAO (GroceryItemDao).
GroceryViewModel:
Contains business logic and data handling operations.
Interacts with GroceryItemDao to perform database operations.
Exposes data to the UI through LiveData.

User Interface:

MainActivity:
Observes LiveData from GroceryViewModel to get updates and modify the UI accordingly.

Interacts with GroceryViewModel to initiate actions like adding or removing items based on user interaction.

ActivityMain.xml:
Defines the layout used by MainActivity (via the bind object), including UI elements like buttons, text views, and a RecyclerView (move elements up and down).

Connections:

GroceryListApplication → GroceryDatabase → GroceryItemDao

GroceryViewModelFactory → GroceryViewModel (uses GroceryItemDao)

MainActivity (uses GroceryViewModelFactory to get GroceryViewModel)

MainActivity ↔ ActivityMain.xml (binds data to UI and receives user interactions)

GroceryViewModel ↔ GroceryItemDao (requests data crud operations)

Visual Representation: The diagram would visually connect these components as follows:

GroceryListApplication is at the top, branching downwards to both MainActivity and GroceryDatabase.

GroceryDatabase connects to GroceryItemDao.

GroceryViewModelFactory sits between MainActivity and GroceryViewModel, indicating its role in creating the ViewModel.

GroceryViewModel connects back to GroceryItemDao to show data flow for database operations.

MainActivity connects to ActivityMain.xml, showing the relationship between the activity and its layout.

Tools for Diagramming:

You can use various tools to create this diagram, such as:

Draw.io (diagrams.net): A free online diagram software that's easy to use for creating architectural diagrams.

Microsoft Visio: A commonly used business diagramming tool that offers extensive features for creating detailed diagrams.

Lucidchart: An online diagramming tool with collaboration features, suitable for team projects.

UMLet: A simple UML tool geared towards fast UML diagramming.

This structured description should help you in crafting the diagram either by hand or using any diagramming tool, aligning with your goals of demonstrating the architecture of an Android application using Room.

1. Introduction to Room Database:

What is Room?
Room is part of the Android Architecture Components.
Provides an abstraction layer over SQLite to allow fluent database access while harnessing SQLite's full power. Gives you simple API methods to do your CRUD stuff.
Includes compile-time checks of SQLite statements.

Benefits of Using Room:
Compile-time verification of SQL queries.
Boilerplate code reduction for database operations.
Easily integrates with LiveData to build data-driven UIs.

2. Project Structure:

Key Components:
GroceryDatabase.kt: This class encapsulates the database setup and serves as the main access point for the underlying SQLite database. It uses the RoomDatabase class to create a singleton instance.
GroceryItemDao.kt: Data Access Object (DAO) that provides methods to interact with the database tables. It uses annotations to bind SQL to each method.
GroceryViewModel.kt: Manages UI-related data in a lifecycle-conscious way. It acts as a communication center between the Repository(ROOM Database) and the UI.
GroceryViewModelFactory.kt: Factory class (meaning we programmatically generate objects base on runtime data) for instantiating the ViewModel with dependencies.
GroceryListApplication.kt: Custom application class to provide dependencies such as the Room database instance.
MainActivity.kt and ActivityMain.xml: UI components where MainActivity binds the layout defined in ActivityMain.xml with data.

3. Flow of Data:

Activity and ViewModel:
MainActivity.kt initializes GroceryViewModel using GroceryViewModelFactory. This setup ensures the ViewModel is supplied with instances of GroceryItemDao.
The activity observes LiveData from the ViewModel, reacting to changes by updating the UI accordingly.
ViewModel and Repository Pattern:
While not explicitly outlined in the provided classes, the repository pattern can be discussed as a best practice.
The repository would act as an additional abstraction layer between the ViewModel and the database or any other data sources.
The purpose of this abstraction layer is that it is a layer of decoupling between the structure and the names in the data store, and the names you called things by in your Program Code.
Database Operations:
The DAO (GroceryItemDao.kt) interfaces with the database directly. The ViewModel uses these DAO methods to retrieve data or update the database, wrapping calls in coroutine blocks to handle operations asynchronously.

4. Connecting the Dots: (Visualizing the systemic interactions between the classes)

From UI to Database:
User interactions in ActivityMain.xml trigger events in MainActivity.kt (e.g., button clicks).
MainActivity uses GroceryViewModel to handle these interactions, which in turn uses GroceryItemDao to execute database operations.
LiveData from GroceryViewModel is observed (observer pattern) in MainActivity to update the UI based on database changes.

5. Practical Session:

Hands-On Implementation:
Students will implement a new feature (e.g., adding a new type of grocery item) to familiarize themselves with the flow from UI to database.
Discussion on the importance of separating concerns using the ViewModel and Repository patterns.

Conclusion:

Understanding how to effectively use Room and structure an Android app using the MVVM pattern is crucial for building robust and maintainable apps.
This lecture provides the foundation for further exploration into Android Architecture Components and more advanced data management strategies in Android development.
This lecture plan gives a comprehensive overview tailored to students new to Android's architecture components, specifically focusing on Room for database management.


image.png

To upgrade your working Grocery List app with the new features like direct item editing, drag-and-drop reordering, and swipe-to-delete, we should follow an incremental approach, adding one feature at a time and testing thoroughly at each stage.
Here's how we can structure the upgrade process:

Step 1: Update the Data Model

If your data model doesn't already support it, you may need to add fields for item order and possibly for category, if you want categorization.

Step 2: Prepare the Room Database

You'll need to create a migration strategy for the Room database if the schema changes, ensuring data isn't lost for existing users. (if you existing code base is in production : you don’t your users to lose all their data when the new app with the new database schema is updated on their device)

Step 3: Adapt the RecyclerView Adapter

Your adapter will require several enhancements:
Implement interfaces to communicate user actions (like edits and swipes) back to the ViewModel.
Add logic for handling item clicks, which will trigger the edit functionality.
Create methods for handling the reordering of items.

Step 4: Edit Functionality

Implement a way for users to edit items.
This could be a dialog that pops up when an item is tapped, allowing users to make changes.

Step 5: Implement Drag-and-Drop and Swipe Actions

Utilize ItemTouchHelper with the RecyclerView to allow users to drag and drop items to reorder them, and swipe to delete.

Step 6: Update the UI and ViewModel

Make sure your UI reflects changes immediately:
Update the ViewModel to handle live updates from the RecyclerView.
Ensure the RecyclerView is updated when the underlying data changes, which can be done by observing LiveData or Flow from the database.

Step 7: Testing

After implementing each feature, test it to ensure it works correctly.
This includes unit tests for the database operations and UI tests for the RecyclerView interactions.

Step 8: Code Review and Refactoring

After adding each feature, review your code to clean up and refactor where necessary. Ensure it follows best practices and is efficient.

Step 9: Documentation and Comments (Make it a part of your practices to be liberal in your use of comments)

Update the code documentation to reflect the new functionalities and any important logic that needs explaining.

Step 10: Version Control

Regularly commit your changes to your version control system. This way, if a new feature introduces issues, you can easily revert to a stable state.
image.png

megaphone

Checklist of Setup:

"Main directory" : where your MainActivity.kt file is:
Create directories:
dao -> GroceryItemDao
database -> GroceryDatabase (mediator to connect to Room)
model -> AppDatabase GroceryItem
ui -> GroceryListAdapter GroceryViewModel
GroceryViewModelFactory
MainActivity
res/layout activity_main.xml grocery_item_layout.xml
values/strings.xml

Let's begin with the first step.
The current structure of your GroceryItem entity:
Let’s review how the data model is set up.

We will learn how to modify it for the new functionalities.


Let’s examinethe current structure of the GroceryItem entity.
Then we will see how to modify it for the new functionalities.
Note you can manipulate the sqlite database on your file system using a tool like:
GroceryItem model : package com.example.grocerylist.model

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "grocery_items")
data class GroceryItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String
)

The GroceryItemDao interface from before is set up for the basic CRUD (Create, Read, Update, Delete) operations using Kotlin coroutines and Flow. This is a good start.
To proceed with adding functionalities like item editing, drag-and-drop reordering, and swipe-to-delete, we will need to look at the GroceryItem entity to ensure it has the necessary fields.
We need to add a position field for ordering and a category field to categorize items.
The updated entity:
@Entity(tableName = "grocery_items")
data class GroceryItem(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
var name: String,
var quantity: Int,
var price: Double,
var category: String, // Add a category field if you need categorization.
var position: Int // Add a position field to keep track of the order.
)

To implement drag-and-drop and swipe-to-delete functionalities, you'd typically use ItemTouchHelper attached to your RecyclerView.
For this, you might need to add methods to your DAO for updating the positions after an item is moved or deleted.

@Query("UPDATE grocery_items SET position = :position WHERE id = :id")
suspend fun updatePosition(id: Long, position: Int)

And for the swipe-to-delete:
@Query("DELETE FROM grocery_items WHERE id = :id")
suspend fun deleteById(id: Long)

Remember that changing the entity will require a migration if you already have a released version of your app.
Since this is a development version, we will skip the migration by uninstalling and reinstalling the app. (We setting up a new emulator with no memory of before).
Once you've updated your entity and DAO, you'll need to implement the logic in your ViewModel and the UI (RecyclerView.Adapter) to handle these actions.

GroceryItem model

GroceryItem model : package com.example.grocerylist.model

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "grocery_items")
data class GroceryItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String
)


Since it currently only includes an id and a name, we'll need to add fields to support editing, reordering, and categorization.
Here's how you can modify your GroceryItem class:
package com.example.grocerylist.model

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "grocery_items")
data class GroceryItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
var name: String,
var quantity: Int = 1, // Default quantity to 1
var price: Double = 0.0, // Default price
var category: String = "", // Add a category field
var position: Int = 0 // Add a position field for ordering
)

Please adjust the default values for quantity, price, category, and position as needed.
Next, update your GroceryItemDao to include methods for handling the position updates and other new fields:
package com.example.grocerylist.dao

import androidx.room.*
import com.example.grocerylist.model.GroceryItem
import kotlinx.coroutines.flow.Flow

@Dao
interface GroceryItemDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: GroceryItem): Long

@Query("SELECT * FROM grocery_items ORDER BY position ASC")
fun getAllItems(): Flow<List<GroceryItem>>

@Update
suspend fun update(item: GroceryItem)

@Delete
suspend fun delete(item: GroceryItem)

@Query("DELETE FROM grocery_items WHERE id = :id")
suspend fun deleteById(id: Int)

@Query("UPDATE grocery_items SET position = :position WHERE id = :id")
suspend fun updatePosition(id: Int, position: Int)

// Add additional queries for category or any other field if necessary
}


With these changes, your data model should now support the required features for your upgraded app.
Remember, if you've already run the app and created a database with the old schema, you'll either need to uninstall the app and reinstall it to clear the old data or provide a migration strategy for your Room database.

Implement the RecyclerView.Adapter changes and add ItemTouchHelper for drag-and-drop, as well as swipe-to-delete functionality.


To implement drag-and-drop and swipe-to-delete functionalities in your RecyclerView, you'll need to work with ItemTouchHelper.

Now we will see the necessary adapter changes and setting up ItemTouchHelper.

First, you'll need to modify your RecyclerView.Adapter to handle changes in item positions and notify the appropriate dataset changes.
Here's an example of how your RecyclerView.Adapter could be structured:
class GroceryAdapter(
private var items: List<GroceryItem>,
private val viewModel: GroceryViewModel // Assuming you have a ViewModel
) : RecyclerView.Adapter<GroceryAdapter.ViewHolder>() {

inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// Bind views
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// Inflate item view and create ViewHolder
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// Bind item data to views
}

override fun getItemCount() = items.size

fun onItemMove(fromPosition: Int, toPosition: Int) {
// Update the item's position in the dataset
if (fromPosition < toPosition) {
for (i in fromPosition until toPosition) {
Collections.swap(items, i, i + 1)
}
} else {
for (i in fromPosition downTo toPosition + 1) {
Collections.swap(items, i, i - 1)
}
}
notifyItemMoved(fromPosition, toPosition)
// TODO: Update the backend data to reflect the position changes
}

fun onItemDismiss(position: Int) {
val id = items[position].id
// Remove the item from the dataset
items = items.filterIndexed { index, _ -> index != position }
notifyItemRemoved(position)
viewModel.deleteItem(id) // Assuming your ViewModel has a method to handle item deletion
}
}

Now, you'll set up the ItemTouchHelper:

class SimpleItemTouchHelperCallback(
private val adapter: GroceryAdapter
) : ItemTouchHelper.Callback() {

override fun isLongPressDragEnabled(): Boolean = true
override fun isItemViewSwipeEnabled(): Boolean = true
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.