Skip to content

Repository files navigation

📱 Smart Tourism MAUI — Audio Guide App

Ứng dụng thuyết minh âm thanh tự động cho Phố Ẩm Thực Vĩnh Khánh, TP. Hồ Chí Minh

A geofencing-triggered, offline-first, multi-language audio tour system for tourists — paired with a Blazor admin dashboard for full content management.


Status License Platform .NET ngrok


🧰 Tech Stack & Languages

Tech stack icons

Layer Badge Version
Mobile App MAUI .NET 10
Backend API ASP.NET Core .NET 10
Admin Dashboard Blazor .NET 10
Shared Library .NET .NET 10
Language C# primary (61.5%)
Database SQLite sqlite-net-pcl
ORM EF Core EF Core SQLite
Map Leaflet OpenStreetMap —
Web (Map/UI) HTML CSS JavaScript 28.7% / 4.6% / 2.7%
Scripts PowerShell 2.5%
Tunnel / Deploy ngrok API tunnel on :5123
App Targets Android Windows Android API 24+ / Windows 10.0.19041+

⚙️ Engine & Framework

The project is built on .NET MAUI (Multi-platform App UI), targeting Android and Windows from a single C# codebase. The backend runs ASP.NET Core on Kestrel; the admin panel uses Blazor Server.

Component Engine Badge
Mobile runtime .NET MAUI (.NET 10) MAUI
Admin web runtime Blazor Server (.NET 10) Blazor
REST API runtime ASP.NET Core (.NET 10) — Kestrel ASP.NET
ORM — server EF Core SQLite EF Core
ORM — mobile sqlite-net-pcl SQLite
Audio TTS — Android Android TextToSpeech engine Android
GPS — Android MAUI Geolocation + Android foreground service Android
Map engine Leaflet.js inside MAUI WebView Leaflet
API tunnel ngrok on port 5123 ngrok

📁 Project Structure

Smart-Tourism-MAUI/
├── 📁 src/                               # Main application projects
│   ├── 📱 MauiApp_Mobile/                # .NET MAUI Mobile App (Android + iOS)
│   ├── 🌐 WebApplication_API/            # ASP.NET Core Backend API
│   ├── 🎨 BlazorApp_AdminWeb/            # Admin Dashboard (Blazor)
│   └── 📚 Project_SharedClassLibrary/    # Shared code
│
├── 🔧 scripts/                           # PowerShell utility scripts
│   ├── run-android-clean.ps1             # Clean Android build
│   ├── start-smarttour-tunnel.ps1        # Start development tunnel
│   └── update-android-network-security-config.ps1
│
└── scripts/                     # PowerShell dev utilities

🔄 App / Web Flow

Mobile App Flow

[App Launch]
     │
     ├─► Load local SQLite (POI data, audio cache)
     ├─► Check network → if online: refresh public catalog from API
     │
     ▼
[Location Service starts]
     │  GPS polling interval follows the selected accuracy mode / foreground service on Android
     ▼
[Geofence Engine — Haversine formula]
     │  Distance check against all POIs (default radius: 30m)
     │  Cooldown: 5 min per POI to prevent audio spam
     ▼
[Audio Decision — Hybrid playback]
     │
     ├─ Tier 1: Cached MP3 on device          → play immediately (offline)
     ├─ Tier 2: TTS script stored locally      → Device TTS
     └─ Tier 3: Optional Gemini speech service → server-side translation/TTS preview
     │
     ▼
[Playback Queue Manager]
     │  Play → Pause → Next → Previous
     │  Auto-pause on OS notification / incoming call
     ▼
[History logged → sync to server when online]

QR Code Direct-Play Flow

[Tourist scans QR at venue]
     │
     ▼
[Deep link → opens app or web landing page]
     │
     ▼
[POI ID resolved → audio plays immediately — no GPS required]

Admin Web Flow

[Admin logs in — session-token auth]
     │
     ├─► POI Management     → Create / Edit / Delete → saved to SQLite via EF Core
     ├─► Audio Management   → Upload MP3 → stored on server → served to mobile
     ├─► Translation Mgmt   → Add multilingual TTS scripts per POI
     ├─► Tour Management    → Group POIs into ordered tours
     ├─► Analytics          → Playback logs, heatmaps, top POIs
     └─► Owner Portal       → Shop owners manage own POI content (restricted RBAC)

🌐 Internal REST API

Base URL: http://<server>:5123 (or https://localhost:7284 when using the HTTPS launch profile)

POI — Points of Interest

Method Endpoint Description
GET /Location List all POIs
GET /Location/{id} Get POI detail
GET /Location/public/catalog Public POI catalog (mobile, no auth)
GET /Location/category/{categoryId} List POIs by category
POST /Location Create POI
PUT /Location/{id} Update POI
DELETE /Location/{id} Delete POI

Audio

Method Endpoint Description
GET /Audio List audio records
GET /Audio/{id} Get audio detail
GET /Audio/public/location/{locationId} Public audio tracks for one POI
GET /Audio/public/location/{locationId}/default Default public audio track
POST /Audio Create or upload audio content
PUT /Audio/{id} Update audio content
DELETE /Audio/{id} Delete audio content

Authentication & Users

Method Endpoint Description
POST /Auth/login Login — returns an admin session token
GET /Auth/me Current admin session
POST /Auth/logout End admin session
GET /DashboardUser List users
POST /DashboardUser Create user
PUT /DashboardUser/{id} Update user / role

Telemetry & Analytics

Method Endpoint Description
POST /Telemetry/v1/route-history Ingest mobile route telemetry
POST /Telemetry/v1/audio-play-events Ingest playback events
POST /Telemetry/v1/heatmap-events Ingest heatmap events
POST /api/v1/analytics/events Ingest usage analytics event
GET /Statistics/top-pois Top POIs by play count
GET /Statistics/heatmap User position heatmap data

Android:

cd src/MauiApp_Mobile
Service Badge Purpose
OpenStreetMap OpenStreetMap Base map tile data
OSRM demo routing OSRM Walking route planning through routing.openstreetmap.de
Gemini Speech Gemini Optional server-side translation and TTS preview
ngrok ngrok Public HTTPS tunnel for the API — device & field testing
MAUI Geolocation / Android foreground service Android GPS polling and background tracking on Android
Android TextToSpeech Android On-device TTS fallback

Gemini speech is disabled by default in appsettings.json; enable it only when you provide a Gemini API key.


✨ Features

📱 Mobile App

Feature Details
GPS Tracking Configurable foreground polling + Android foreground service
Geofencing Haversine formula · configurable radius (default 30m) · 5-min cooldown
Hybrid Audio Playback Cached MP3 / downloaded audio → local TTS script → optional Gemini speech
Audio Queue Play, pause, next, previous · auto-pause on notification / incoming call
Multi-language 🇻🇳 Vietnamese · 🇬🇧 English · 🇨🇳 Chinese · 🇯🇵 Japanese · 🇰🇷 Korean
Offline Mode Local SQLite catalog cache + downloaded/cached audio
Telemetry Sync Queued route, playback, listening-session, heatmap, and usage events sync when connectivity is restored
QR Code Scan Scan QR at a venue → plays that POI's audio directly, bypassing GPS
Interactive Map Leaflet + OpenStreetMap in WebView · all POIs + current location
Settings Language selector · GPS sensitivity · TTS voice · offline pack download

🎛️ Admin Dashboard

Feature Details
POI CRUD Create, edit, delete POIs with coordinates, radius, priority, images
Audio Management Upload pre-recorded MP3 files · manage TTS scripts per language
Translation Management Add/edit multilingual content per POI
Tour Management Group POIs into ordered tours
User & Role Management Admin assignment · shop owner verification · RBAC
Analytics Dashboard Top POIs by play count · average listen time · date range filtering
Heatmap Visual heatmap of user positions across the food street
Owner Portal Self-service: shop owners edit their own POI and upload audio

🗄️ Database

SQLite

Table Used by Purpose
Locations / CachedLocations Server + Mobile Points of interest: name, coords, radius, priority, category
AudioContents / CachedAudioTracks Server + Mobile Audio records, scripts, language, source type, priority
Categories / CachedCategories Server + Mobile POI category metadata
Languages Server Managed language records
Tours, TourLocations Server Grouped POI tours with ordering and route data
DashboardUsers Server Admin/owner/user accounts and roles
PlaybackEvents, AudioListeningSessions Server Playback and listening analytics
LocationTrackingEvents, HeatmapEvents, UsageEvents Server + Mobile queue Telemetry and usage analytics
ChangeRequests, InboxMessages, ActivityLogs Server Owner moderation workflow, notifications, and audit trail
LocalSettings, DeviceSyncStates, PlaybackHistory Mobile User preferences, catalog sync state, and local history

🚀 Getting Started

Prerequisites

VS 2022 .NET Android SDK SQLite ngrok

Clone & Restore

git clone https://github.com/DZT711/Smart-Tourism-MAUI.git
cd Smart-Tourism-MAUI
dotnet restore Smart-Tourism-MAUI.sln

1 — Backend API

# Uses WebApplication_API/appsettings.json (default SQLite: Data Source=App.db)
dotnet run --project WebApplication_API/WebApplication_API.csproj --launch-profile http

# Expose for device testing (optional)
ngrok http 5123 --host-header="localhost:5123"

2 — Admin Dashboard

cd src/WebApplication_API

# Build
dotnet build -c Debug

### 3 — Mobile App

Windows (quick test):

dotnet run --project MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-windows10.0.19041.0

Android — USB cable:

adb devices                        # confirm device shows as "device"
adb reverse tcp:5123 tcp:5123      # forward API port to device
dotnet run --project MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-android

Android — Wi-Fi:

adb connect <device-ip>:<port>
adb devices
# Server URLs are configured in MauiApp_Mobile/Resources/Raw/mobile-api.json.
# Debug Android builds also update network_security_config.xml before build.
dotnet run --project MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-android

Pull SQLite DB from device:

adb exec-out run-as com.companyname.mauiapp_mobile cat files/smarttour-mobile.db3 > docs/smarttour-mobile.db

4 — Database Setup

SQLite databases are created automatically on first run — no manual setup needed.

The API applies EF Core migrations and seeds baseline admin users, POIs, audio, tours, and analytics samples at startup.

cd src/BlazorApp_AdminWeb

# Debug build
dotnet build -c Debug

# Server SQLite — optional manual migration command
cd WebApplication_API
dotnet ef database update

# Optional SQL sample data lives in docs/DatabaseStructure/sample-data.sql

🌐 Publishing & Deployment with ngrok

ngrok

ngrok creates a secure public HTTPS tunnel to the local ASP.NET Core API running on port 5123, making it reachable from physical Android devices and external testers without configuring firewalls, port forwarding, or a cloud server.

Why ngrok?

Scenario Without ngrok With ngrok
Android device on same Wi-Fi Needs LAN IP (192.168.x.x) — may still fail on restricted networks Single stable HTTPS URL
Android device on mobile data ❌ Unreachable ✅ Works from anywhere
Share API with a team member ❌ Not possible without VPN ✅ Send them the tunnel URL
Test push-style telemetry sync Requires static IP or cloud VM ✅ ngrok URL + inspect dashboard
Admin dashboard from a phone Must be on the same LAN ✅ Any browser, any network

Installation

Windows (winget):

winget install ngrok.ngrok

Windows (Chocolatey):

choco install ngrok

macOS (Homebrew):

brew install ngrok/ngrok/ngrok

Linux:

curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
  | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
  && echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
  | sudo tee /etc/apt/sources.list.d/ngrok.list \
  && sudo apt update && sudo apt install ngrok

Or download the binary directly from ngrok.com/download.

Account & Auth Token (one-time setup)

  1. Sign up for a free account at dashboard.ngrok.com.
  2. Copy your Authtoken from the dashboard.
  3. Register it locally:
ngrok config add-authtoken <YOUR_AUTHTOKEN>

Start the Tunnel

Run the API first, then open the tunnel in a separate terminal:

# Terminal 1 — start the API
dotnet run --project WebApplication_API/WebApplication_API.csproj --launch-profile http
# Kestrel listens on http://localhost:5123

# Terminal 2 — open the ngrok tunnel
ngrok http 5123 --host-header="localhost:5123"

ngrok will print output like:

Forwarding  https://a1b2-103-xxx-xxx-xxx.ngrok-free.app -> http://localhost:5123

The https:// URL is your public API endpoint. Copy it.

Configure the Mobile App to Use the Tunnel URL

Open MauiApp_Mobile/Resources/Raw/mobile-api.json and replace the base URL:

{
  "ApiBaseUrl": "https://a1b2-103-xxx-xxx-xxx.ngrok-free.app",
  "PublicCatalogEndpoint": "/Location/public/catalog",
  "AudioEndpoint": "/Audio/public/location"
}

Note: The free ngrok tier generates a new random URL every time you restart the tunnel. Paste the new URL into mobile-api.json and redeploy, or upgrade to a paid ngrok plan to get a static subdomain (e.g., https://smart-tourism.ngrok.app).

Configure the Admin Dashboard to Use the Tunnel URL

Open BlazorApp_AdminWeb/appsettings.json:

{
  "ApiSettings": {
    "BaseUrl": "https://a1b2-103-xxx-xxx-xxx.ngrok-free.app"
  }
}

ASP.NET Core — Allow the ngrok Host Header

Add the following to WebApplication_API/appsettings.json so Kestrel accepts requests forwarded by ngrok:

{
  "AllowedHosts": "*"
}

Or configure forwarded headers in Program.cs to preserve the original scheme and host:

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

ngrok Inspection Dashboard

While the tunnel is running, open http://127.0.0.1:4040 in your browser to see every HTTP request in real time — headers, payloads, status codes, and timing. This is especially useful for debugging telemetry sync from the mobile app.

ngrok Configuration File (optional)

For a one-command startup of both the API tunnel and the admin dashboard tunnel, create ~/.config/ngrok/ngrok.yml (or %HOMEPATH%\AppData\Local/ngrok/ngrok.yml on s):

version: "3"
agent:
  authtoken: <YOUR_AUTHTOKEN>

tunnels:
  api:
    proto: http
    addr: 5123
    host_header: "localhost:5123"
    # schemes: [https]          # force HTTPS only (paid plans)
    # domain: smart-tourism.ngrok.app  # static domain (paid plans)

  admin:
    proto: http
    addr: 5223
    host_header: "localhost:5223"

Start all tunnels at once:

ngrok start --all

Free vs Paid ngrok Plans

Feature Free Paid
HTTPS tunnel ✅ ✅
Random URL (changes on restart) ✅ ✅
Static / custom subdomain ❌ ✅
Simultaneous tunnels 1 Multiple
Requests per minute Rate-limited Higher limits
TCP tunnels ❌ ✅
IP restrictions ❌ ✅

For academic/demo use the free tier is sufficient. For a persistent field deployment at Phố Ẩm Thực Vĩnh Khánh, a static domain on a paid plan or a proper cloud VM (e.g., Azure App Service, Railway, Fly.io) is recommended.

Tunnel Architecture Diagram

[Android Device / Tester Browser]
          │  HTTPS :443
          ▼
  ┌───────────────────────┐
  │  ngrok Edge (Cloud)   │   https://xxxx.ngrok-free.app
  └───────────┬───────────┘
              │  encrypted tunnel
              ▼
  ┌───────────────────────┐
  │  ngrok Agent (local)  │   running on dev machine
  └───────────┬───────────┘
              │  HTTP
              ▼
  ┌───────────────────────┐
  │  ASP.NET Core Kestrel │   http://localhost:5123
  │  WebApplication_API   │
  └───────────────────────┘

📖 Documentation

Document Purpose
docs/specification.md Full feature specification & user stories
docs/DatabaseStructure/ SQLite schema, migrations, sample data
docs/Diagram/ Architecture & flow diagrams

📜 License

License: CC BY-NC-SA 4.0

Creative Commons Attribution – NonCommercial – ShareAlike 4.0 International

Copyright © 2026 Nguyễn Sĩ Huy (3123411122) & Nguyễn Văn Cường (3123411045) Khoa Công nghệ Thông tin — Dự Án Thuyết Minh Phố Ẩm Thực Vĩnh Khánh

You are free to:

  • Share — copy and redistribute this material in any medium or format
  • Adapt — remix, transform, and build upon the material

Under the following terms:

  • Attribution — Give appropriate credit and link to this repository.
  • NonCommercial — You may not use the material for commercial purposes.
  • ShareAlike — Derivatives must be distributed under the same license.

Full license: https://creativecommons.org/licenses/by-nc-sa/4.0/

This project was built as an academic capstone at Ho Chi Minh City University of Technology and Education (UTE) and is intended for non-commercial, educational, and cultural heritage use only.


🙏 Acknowledgments

OpenStreetMap Leaflet OSRM ngrok

Special thanks to Vinh Khanh Food Street (Phố Ẩm Thực Vĩnh Khánh) as the project location and primary stakeholder.

E.Cách chạy dự án

  1. Backend API:

    • Mở src/WebApplication_API trong Visual Studio.
    • Cấu hình chuỗi kết nối SQL Server trong appsettings.json.
    • Chạy migrations để tạo database.
    • Chạy ứng dụng (F5) → API sẽ chạy trên https://localhost:5123.
     dotnet run --project src/WebApplication_API/WebApplication_API.csproj --urls "https://0.0.0.0:5123"
     ngrok http 5123 --host-header="localhost:5123"
  2. Admin Web:

    • Mở src/BlazorApp_AdminWeb trong Visual Studio.
    • Cấu hình appsettings.json để trỏ đến API. Mặc định project đang dùng https://localhost:5123/.
    • Chạy ứng dụng → Đăng nhập bằng tài khoản admin đã seed sẵn.
     dotnet run --project src/BlazorApp_AdminWeb/BlazorApp_AdminWeb.csproj 
    • Tài khoản thử nghiệm : username:admin/ password:admin
  3. Mobile App:

    • Mở src/MauiApp_Mobile trong Visual Studio/VSCode.
    • Cấu hình ApiEndpoints.cs để trỏ đến API.
    • Chạy ứng dụng trên Android Emulator hoặc thiết bị thật.

    Cách 1: chạy trên windows :

        dotnet run --project src/MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-windows10.0.19041.0

    Cách 2: chạy trên Android cắm cáp usb vào máy chủ

    • Cho phép máy tính debug trên android(bật dev mode trong setting)
         adb devices (đảm bảo thiết bị ở trạng thái mở "device")
         adb reverse tcp:5123 tcp:5123 (để chuyển tiếp cổng từ máy chủ đến thiết bị)
         dotnet run --project src/MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-android
    • Thử gọi api: "http://127.0.0.1:5123/location/public/catalog" Cách 3: chạy thông qua wifi
    • Bật Wifi Debug trên android : lấy thông tin ip và cổng kết nối
    • Chỉnh mạng máy sever thanh private
    • Đảm bảo sử dụng chung 1 mạng
    • Thêm ip sever vào file cấu hình mạng của android trong src/MauiApp_Mobile/Platforms/Android/Resources/xml/network_security_config.xml
            <domain includeSubdomains="false">yourSeverIP</domain>
        adb connect ip:port (kết nối qua wifi, ví dụ adb connect 192.168.x.x:44444)
        adb devices (đảm bảo thiết bị ở trạng thái mở "device")
        ipconfig(lấy ipv4 của máy sever ipsever )
        dotnet run --project src/MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-android

    -Thử gọi api: "http://ipsever:5123/location/public/catalog" Lệnh pull db từ điện thoại kêt nối

        adb exec-out run-as com.companyname.mauiapp_mobile cat files/smarttour-mobile.db3 > docs\smarttour-mobile.db

© 2026 — Nguyễn Sĩ Huy (3123411122) & Nguyễn Văn Cường (3123411045) Dự Án Thuyết Minh Phố Ẩm Thực Vĩnh Khánh — Khoa Công nghệ Thông tin Last Updated: April 23, 2026
Current Branch: Mobile_AppPerformance
Status: 🟡 Active Development

Last Updated: May 2026 · Status: 🟡 Active Development

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages