Building a Food Delivery App with Local Data Storage and Server Integration

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:

  1. Local Storage: SQLite data base for offline functionality
  2. 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.

Tags: Android sqlite JSON Mobile Development Food Delivery

Posted on Mon, 22 Jun 2026 16:53:52 +0000 by kkibak