Ứ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.
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.
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 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]
[Tourist scans QR at venue]
│
▼
[Deep link → opens app or web landing page]
│
▼
[POI ID resolved → audio plays immediately — no GPS required]
[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)
Base URL: http://<server>:5123 (or https://localhost:7284 when using the HTTPS launch profile)
Android:
cd src/MauiApp_MobileGemini speech is disabled by default in
appsettings.json; enable it only when you provide a Gemini API key.
| 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 |
| 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 |
| 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 |
git clone https://github.com/DZT711/Smart-Tourism-MAUI.git
cd Smart-Tourism-MAUI
dotnet restore Smart-Tourism-MAUI.sln# 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"cd src/WebApplication_API
# Build
dotnet build -c Debug
### 3 — Mobile AppWindows (quick test):
dotnet run --project MauiApp_Mobile/MauiApp_Mobile.csproj -f net10.0-windows10.0.19041.0Android — 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-androidAndroid — 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-androidPull SQLite DB from device:
adb exec-out run-as com.companyname.mauiapp_mobile cat files/smarttour-mobile.db3 > docs/smarttour-mobile.dbSQLite 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.sqlngrok 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.
| 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 |
Windows (winget):
winget install ngrok.ngrokWindows (Chocolatey):
choco install ngrokmacOS (Homebrew):
brew install ngrok/ngrok/ngrokLinux:
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 ngrokOr download the binary directly from ngrok.com/download.
- Sign up for a free account at dashboard.ngrok.com.
- Copy your Authtoken from the dashboard.
- Register it locally:
ngrok config add-authtoken <YOUR_AUTHTOKEN>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.
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.jsonand redeploy, or upgrade to a paid ngrok plan to get a static subdomain (e.g.,https://smart-tourism.ngrok.app).
Open BlazorApp_AdminWeb/appsettings.json:
{
"ApiSettings": {
"BaseUrl": "https://a1b2-103-xxx-xxx-xxx.ngrok-free.app"
}
}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
});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.
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| 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.
[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 │
└───────────────────────┘
| Document | Purpose |
|---|---|
docs/specification.md |
Full feature specification & user stories |
docs/DatabaseStructure/ |
SQLite schema, migrations, sample data |
docs/Diagram/ |
Architecture & flow diagrams |
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.
Special thanks to Vinh Khanh Food Street (Phố Ẩm Thực Vĩnh Khánh) as the project location and primary stakeholder.
-
Backend API:
- Mở
src/WebApplication_APItrong 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"
- Mở
-
Admin Web:
- Mở
src/BlazorApp_AdminWebtrong Visual Studio. - Cấu hình
appsettings.jsonđể trỏ đến API. Mặc định project đang dùnghttps://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
- Mở
-
Mobile App:
- Mở
src/MauiApp_Mobiletrong 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 - Mở
© 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