Files
travelplanning/ANDROID_BUILD_GUIDE..md
T

7.7 KiB

🗺️ 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/:

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
<manifest xmlns:android="http://schemas.microsoft.com/apk/res/android">
    
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature android:name="android.hardware.location.gps" />

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
    
    <queries>
        <intent>
            <action android:name="android.media.action.IMAGE_CAPTURE" />
        </intent>
    </queries>

    <application
        ...
        android:usesCleartextTraffic="true"> ...
    </application>
</manifest>

## 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<string | undefined>();

  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 (
    <IonPage>
      <IonContent className="ion-padding">
        <div className="flex flex-col items-center gap-4">
          <h1 className="text-xl font-bold">Tính năng Chụp ảnh</h1>
          
          <IonButton onClick={takeAndSavePhoto} color="primary">
            <IonIcon slot="start" icon={camera}></IonIcon>
            Chụp và Lưu Ảnh Gốc
          </IonButton>

          {photoUri && (
            <div className="mt-4 border p-2">
              <p>Xem trước ảnh vừa chụp:</p>
              <img src={photoUri} alt="Xem trước ảnh chụp" className="max-w-xs mt-2" />
            </div>
          )}
        </div>
      </IonContent>
    </IonPage>
  );
};

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