# 🗺️ Hướng Dẫn Đóng Gói & Kiểm Thử Ứng Dụng Android (YoTrip) Tài liệu này hướng dẫn chi tiết quy trình thiết lập môi trường máy tính Windows Local để biên dịch, kiểm thử dự án Frontend (React + Vite) qua Capacitor, kết nối tới hệ thống Docker Production (`https://yotrip.labz.io.vn`) và chuẩn bị phát hành lên Google Play Store. --- ## 📋 1. Điều Kiện Tiên Quyết (Môi Trường Windows) Trước khi chạy lệnh, đảm bảo máy tính local đã cài đặt và cấu hình đầy đủ các công cụ sau: * **Node.js:** Phiên bản v18 hoặc v20+. * **Java JDK:** Phiên bản 17 hoặc 21 (Temurin hoặc Microsoft OpenJDK). * Biến môi trường hệ thống: `JAVA_HOME` trỏ tới thư mục cài đặt JDK. * Biến `Path` hệ thống: Bổ sung `%JAVA_HOME%\bin`. * **Android Studio:** * Đã cài đặt **Android SDK**, **Android SDK Command-line Tools**. * Biến môi trường hệ thống: `ANDROID_HOME` trỏ tới `AppData\Local\Android\Sdk`. * Đã khởi tạo 1 thiết bị ảo (Android Simulator) qua *Virtual Device Manager*. --- ## ⚙️ 2. Cấu Hình Mã Nguồn Frontend (Local) ### 2.1 Cấu hình file `.env.production` Tạo hoặc cập nhật file `.env.production` nằm tại thư mục gốc của `frontend/`: ```text VITE_BACKEND_URL=[https://yotrip.labz.io.vn](https://yotrip.labz.io.vn) ### 2.2 Cấu hình Axios / API Instance (src/api/axios.ts) Cập nhật logic baseURL để tự động phân tách môi trường chạy Web Dev (sử dụng Proxy của Vite) và môi trường chạy App Native (gọi trực tiếp URL tuyệt đối): import axios from 'axios'; import { Capacitor } from '@capacitor/core'; const API = axios.create({ baseURL: Capacitor.isNativePlatform() ? import.meta.env.VITE_BACKEND_URL : '', timeout: 15000, headers: { 'Content-Type': 'application/json', }, }); export default API; ## 3. Cấu Hình Nền Tảng Android Native ### 3.1 Thiết lập Biểu tượng Ứng dụng (App Icon) Để sử dụng frontend/public/favicon.ico làm icon của app trên Android, chúng ta cần chuyển đổi nó sang định dạng .png độ phân giải cao và sử dụng công cụ của Capacitor để tự động tạo các kích thước cần thiết cho Android. Chuẩn bị ảnh: Chuyển đổi file favicon.ico của bạn thành file .png (khuyên dùng độ phân giải ít nhất 1024x1024 pixel để có chất lượng tốt nhất trên các thiết bị đời mới) và lưu tên là icon-only.png. Cài đặt công cụ: Chạy lệnh sau tại thư mục frontend/ để cài đặt công cụ quản lý tài nguyên của Capacitor: Bash npm install @capacitor/assets --save-dev Khởi tạo thư mục: Tạo thư mục assets ở thư mục gốc của frontend/ (cùng cấp với src) và đặt file icon-only.png vào đó. Bash mkdir assets # Sau đó di chuyển file icon-only.png của bạn vào thư mục assets/ Tạo Icon: Chạy lệnh sau để tự động tạo và đặt các icon vào đúng vị trí trong dự án Android: Bash npx capacitor-assets generate --android ### 3.2 File capacitor.config.json Định danh chính xác gói ứng dụng (App ID) dùng để đăng ký trên Google Play Console: { "appId": "com.yotrip.app", "appName": "YoTrip", "webDir": "dist", "plugins": { "SplashScreen": { "launchShowDuration": 2000 } } } ### 3.3 File capacitor.config.json Định danh chính xác gói ứng dụng (App ID) dùng để đăng ký trên Google Play Console: JSON { "appId": "com.yotrip.app", "appName": "YoTrip", "webDir": "dist", "plugins": { "SplashScreen": { "launchShowDuration": 2000 } } } ### 3.4 Cấu hình quyền trong AndroidManifest.xml Mở đường dẫn android/app/src/main/AndroidManifest.xml, thêm các quyền truy cập Internet, định vị GPS, và các quyền cần thiết cho tính năng chụp ảnh và lưu ảnh vào bộ nhớ máy: XML ... ## 4. Tích Hợp Tính Năng Chụp Ảnh và Lưu Ảnh (React Code) Sử dụng Plugin của Capacitor để tích hợp trực tiếp vào code React của bạn. Cài đặt Plugin Camera: Chạy lệnh sau tại thư mục frontend/: Bash npm install @capacitor/camera npx cap update Ví dụ code: Dưới đây là cách implement tính năng chụp ảnh và tự động lưu ảnh gốc vào thư viện ảnh của điện thoại trong một React component (ví dụ src/components/PhotoTaker.tsx): JavaScript import React, { useState } from 'react'; import { IonButton, IonIcon, IonContent, IonPage } from '@ionic/react'; import { camera } from 'ionicons/icons'; import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; const PhotoTaker: React.FC = () => { const [photoUri, setPhotoUri] = useState(); const takeAndSavePhoto = async () => { try { const image = await Camera.getPhoto({ quality: 90, allowEditing: false, // Giữ nguyên ảnh gốc, không qua chỉnh sửa resultType: CameraResultType.Uri, source: CameraSource.Camera, // Mở camera trực tiếp saveToGallery: true, // YÊU CẦU MỚI: Tự động lưu ảnh gốc vào thư viện điện thoại }); // Bạn có thể sử dụng image.webPath để hiển thị xem trước setPhotoUri(image.webPath); console.log('Ảnh đã được chụp và lưu tại:', image.path); } catch (error) { console.error('Lỗi khi chụp hoặc lưu ảnh:', error); } }; return (

Tính năng Chụp ảnh

Chụp và Lưu Ảnh Gốc {photoUri && (

Xem trước ảnh vừa chụp:

Xem trước ảnh chụp
)}
); }; export default PhotoTaker; ## 5. Quy Trình Biên Dịch & Kiểm Thử (Simulator) Mỗi lần cập nhật code giao diện ở máy local, chạy chuỗi lệnh sau tại Terminal của VS Code để đẩy app lên máy ảo: # Bước 1: Cài đặt các thư viện phụ thuộc tại local npm install # Bước 2: Build code React + Vite thành file tĩnh npm run build # Bước 3: Đồng bộ mã nguồn tĩnh vào thư mục Android mã nguồn mở npx cap sync # Bước 4: Khởi chạy máy ảo và nạp ứng dụng tự động npx cap run android