Project Overview
This project implements a food delivery application for Android with features including user authentication, store browsing, product ordering, cart management, and order history tracking. The system uses local SQLite storage alongside server-side MySQL database integration.
System Requirements
The application must provide:
- Product catalog display with detailed item information
- User registration and login functionality
- Shopping cart operations and order placement
- Order history retrieval
- Data persistence using both local and remote storage solutions
Core Components
Database Architecture
The data layer utilizes dual storage mechanisms:
- Local Storage: SQLite data base for offline functionality
- Remote Storage: MySQL database via Spring Boot backend
The SQLite helper class extends SQLiteOpenHelper to manage local data:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "delivery_app.db";
private static final int VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"username TEXT, password TEXT, balance INTEGER)");
db.execSQL("CREATE TABLE orders (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"user_id INTEGER, items TEXT, total_amount INTEGER, timestamp TEXT)");
db.execSQL("CREATE TABLE merchants (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"code TEXT, name TEXT, min_order INTEGER, delivery_fee INTEGER)");
db.execSQL("CREATE TABLE products (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"merchant_code TEXT, code TEXT, name TEXT, price INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Migration logic would go here
}
}
Data Models
Static JSON resources define merchant and product catalogs:
merchants.json
[
{"code":"coffee_shop", "name":"Urban Cafe","monthly_sales":"1,200 orders","min_order":20,"delivery_fee":3,"rating":"4.8 stars","eta":"25 mins","promo":"Free delivery on orders over $30"},
{"code":"burger_king", "name":"Royal Burgers","monthly_sales":"850 orders","min_order":15,"delivery_fee":2,"rating":"4.6 stars","eta":"30 mins","promo":"Combo meals 20% off"}
]
coffee_shop.json
[
{"code":"latte", "name":"Classic Latte","sales":"320 sold","rating":"98% positive","popularity":"Bestseller","price":5},
{"code":"croissant", "name":"Butter Croissant","sales":"180 sold","rating":"95% positive","popularity":"Freshly baked","price":3}
]
JSON Parsing Services
Data parsing utilities convert JSON resources into Java objects:
public class MerchantParser {
public static List<Merchant> parseMerchants(InputStream stream) throws IOException {
byte[] buffer = new byte[stream.available()];
stream.read(buffer);
String jsonString = new String(buffer, "UTF-8");
Gson gson = new Gson();
Type listType = new TypeToken<List<Merchant>>(){}.getType();
return gson.fromJson(jsonString, listType);
}
public static Merchant parseMerchant(String jsonString) {
Gson gson = new Gson();
Type type = new TypeToken<Merchant>(){}.getType();
return gson.fromJson(jsonString, type);
}
}
Product parsing follows similar pattern:
public class ProductParser {
public static List<Product> parseProducts(InputStream stream) throws IOException {
byte[] buffer = new byte[stream.available()];
stream.read(buffer);
String jsonString = new String(buffer, "UTF-8");
Gson gson = new Gson();
Type listType = new TypeToken<List<Product>>(){}.getType();
return gson.fromJson(jsonString, listType);
}
}
Resource Loading
Dynamic resource loading maps identifiers to drawable assets:
private int getDrawableResourceId(String identifier) {
try {
Field field = R.drawable.class.getField(identifier);
return field.getInt(field.getName());
} catch (Exception e) {
return R.drawable.default_image;
}
}
private int getRawResourceId(String filename) {
try {
Field field = R.raw.class.getField(filename);
return field.getInt(field.getName());
} catch (Exception e) {
return 0;
}
}
Loading merchant data during initialization:
InputStream merchantStream = getResources().openRawResource(R.raw.merchants);
List<Merchant> merchants = MerchantParser.parseMerchants(merchantStream);
for (Merchant merchant : merchants) {
merchant.setImageId(getDrawableResourceId(merchant.getCode()));
}
Loading product data based on merchant selection:
int resourceId = getRawResourceId(selectedMerchant.getCode());
InputStream productStream = getResources().openRawResource(resourceId);
List<Product> products = ProductParser.parseProducts(productStream);
for (Product product : products) {
product.setImageId(getDrawableResourceId(product.getCode()));
}
UI Implementation
Store listing interface uses ListView with custom adapter:
Layout Structure
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#2196F3"
android:text="Stores"
android:textColor="#FFFFFF"
android:gravity="center"
android:textSize="20sp" />
<ImageView
android:id="@+id/profile_button"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:background="@drawable/user_icon" />
<ListView
android:id="@+id/store_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/header"
android:divider="#E0E0E0"
android:dividerHeight="1dp" />
</RelativeLayout>
List Item Template
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="@+id/store_logo"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginEnd="16dp"
android:background="@drawable/default_store" />
<TextView
android:id="@+id/store_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/store_logo"
android:text="Store Name"
android:textSize="18sp"
android:textColor="#000000" />
<TextView
android:id="@+id/sales_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/store_name"
android:layout_toEndOf="@id/store_logo"
android:text="Monthly Sales"
android:textSize="14sp" />
<TextView
android:id="@+id/delivery_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/sales_info"
android:layout_toEndOf="@id/store_logo"
android:text="Delivery Info"
android:textSize="14sp" />
<TextView
android:id="@+id/rating_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/store_logo"
android:layout_marginTop="8dp"
android:background="#FFC107"
android:padding="4dp"
android:text="Rating"
android:textColor="#FF5722"
android:textSize="12sp" />
</RelativeLayout>
Custom Adapter Pattern
The adapter binds data to UI components efficiantly:
private class StoreAdapter extends BaseAdapter {
@Override
public int getCount() {
return merchantList.size();
}
@Override
public Object getItem(int position) {
return merchantList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(getApplicationContext())
.inflate(R.layout.store_item, parent, false);
holder = new ViewHolder();
holder.name = convertView.findViewById(R.id.store_name);
holder.sales = convertView.findViewById(R.id.sales_info);
holder.delivery = convertView.findViewById(R.id.delivery_info);
holder.rating = convertView.findViewById(R.id.rating_badge);
holder.logo = convertView.findViewById(R.id.store_logo);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Merchant merchant = merchantList.get(position);
holder.name.setText(merchant.getName());
holder.sales.setText(merchant.getMonthlySales());
holder.delivery.setText("Min order: $" + merchant.getMinOrder() +
" | Delivery: $" + merchant.getDeliveryFee());
holder.rating.setText(merchant.getRating());
holder.logo.setImageResource(merchant.getImageId());
return convertView;
}
private class ViewHolder {
TextView name, sales, delivery, rating;
ImageView logo;
}
}
Navigation Handling
Click events trigger transitions between views:
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Merchant selectedMerchant = merchantList.get(position);
Intent intent = new Intent(this, ProductActivity.class);
intent.putExtra("merchant_data", new Gson().toJson(selectedMerchant));
intent.putExtra("user_session", currentUser);
startActivityForResult(intent, REQUEST_CODE);
}
Backend Integration
Spring Boot server provides REST endpoints for user synchronization:
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody UserData user) {
// Registration logic
return ResponseEntity.ok().build();
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// Authentication logic
return ResponseEntity.ok().build();
}
}
Client-server communication occurs during authentication flows. For simplified local testing, network calls can be disabled through conditional compilation.