Initial commit: Set up retro photobooth monorepo structure, core configurations, and premium sample assets
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
# --- OS Files ---
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.swp
|
||||
|
||||
# --- Android Studio / Gradle ---
|
||||
.gradle/
|
||||
build/
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
local.properties
|
||||
*.apk
|
||||
*.aar
|
||||
*.bms
|
||||
.idea/workspace.xml
|
||||
.idea/libraries/
|
||||
.idea/caches/
|
||||
|
||||
# --- Xcode / iOS ---
|
||||
build/
|
||||
DerivedData/
|
||||
*.xcodeproj/*
|
||||
!*.xcodeproj/project.pbxproj
|
||||
!*.xcodeproj/xcshareddata/
|
||||
*.xcworkspace/
|
||||
!*.xcworkspace/contents.xcworkspacedata
|
||||
.strongspace/
|
||||
project.xcworkspace/
|
||||
xcuserdata/
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprint
|
||||
@@ -0,0 +1,28 @@
|
||||
# Kế hoạch Phát triển Dự án Mobile Photobooth (Vintage Style)
|
||||
|
||||
Tài liệu này ghi nhận kế hoạch phát triển qua các giai đoạn (sprint) cho ứng dụng Mobile Photobooth.
|
||||
|
||||
## 📅 Giai Đoạn 1 (Sprint 1 - Tuần 1-2)
|
||||
|
||||
* **Thiết lập cấu trúc monorepo & quyền:** Hoàn thành việc tạo thư mục và các cấu hình quyền Android/iOS.
|
||||
* **Chuẩn bị dữ liệu mẫu:** Tải các font chữ vintage, khung hình Instax mini và stickers.
|
||||
* **Màn hình Preview Camera (Android):** Code màn hình Preview Camera cơ bản sử dụng Jetpack Compose và CameraX.
|
||||
* **Tích hợp KVM trên emulator:** Cấu hình chạy thử nghiệm.
|
||||
|
||||
## 📅 Giai Đoạn 2 (Sprint 2 - Tuần 3-4)
|
||||
|
||||
* **Bộ lọc Vintage (Filters):** Phát triển bộ lọc màu vintage (sepia, monochrome, warm tones).
|
||||
* **Trang trí (Sticker & Frame):** Cho phép người dùng kéo thả sticker và chọn khung ảnh Instax/Photobooth 4-strip.
|
||||
* **Lưu và chia sẻ:** Xuất ảnh chất lượng cao và lưu vào thư viện thiết bị.
|
||||
|
||||
## 💡 Cấu trúc dữ liệu vị trí Sticker (Đề xuất)
|
||||
Để đồng bộ tọa độ sticker giữa iOS và Android, sử dụng phần trăm vị trí thay vì pixel:
|
||||
```json
|
||||
{
|
||||
"sticker_id": "sunglasses",
|
||||
"x_percent": 0.25,
|
||||
"y_percent": 0.40,
|
||||
"scale": 1.2,
|
||||
"rotation_degree": 45
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,198 @@
|
||||
# 📋 KẾ HOẠCH PHÁT TRIỂN DỰ ÁN: MOBILE PHOTOBOOTH (VINTAGE STYLE)
|
||||
|
||||
Dự án ứng dụng Photobooth di động đa nền tảng (Native iOS/Android), lưu trữ mã nguồn tập trung trên Debian Linux, ưu tiên phát triển, build và kiểm thử trên Android trước.
|
||||
|
||||
---
|
||||
|
||||
## 📁 1. Cấu Trúc Thư Mục Dự Án (Monorepo Structure)
|
||||
|
||||
Tạo cấu trúc thư mục này tại thư mục gốc trên Debian Server để quản lý chung toàn bộ tài nguyên và mã nguồn của cả hai nền tảng.
|
||||
|
||||
```text
|
||||
photobooth-project/
|
||||
├── .gitignore
|
||||
├── README.md
|
||||
├── PLAN.md
|
||||
├── assets/ # Tài nguyên dùng chung cho cả iOS và Android
|
||||
│ ├── fonts/ # Font chữ vintage, viết tay (.ttf, .otf)
|
||||
│ │ ├── CourierPrime-Regular.ttf
|
||||
│ │ └── HomemadeApple-Regular.ttf
|
||||
│ ├── frames/ # Khung ảnh định dạng PNG-24 trong suốt
|
||||
│ │ ├── instax_mini_single.png
|
||||
│ │ └── photobooth_4strip.png
|
||||
│ └── stickers/ # Icon phụ kiện, smileys dạng vector/PNG cao cấp
|
||||
│ ├── sunglasses.png
|
||||
│ └── vintage_heart.svg
|
||||
│
|
||||
├── android-project/ # Dự án Android Native (Mở bằng Android Studio)
|
||||
│ ├── build.gradle.kts
|
||||
│ ├── settings.gradle.kts
|
||||
│ └── app/
|
||||
│ ├── build.gradle.kts
|
||||
│ └── src/
|
||||
│ └── main/
|
||||
│ ├── AndroidManifest.xml
|
||||
│ └── java/com/photobooth/app/ # Mã nguồn Kotlin
|
||||
│
|
||||
└── ios-project/ # Dự án iOS Native (Mở bằng Xcode trên Mac)
|
||||
├── PhotoboothApp.xcodeproj
|
||||
└── PhotoboothApp/
|
||||
├── Info.plist
|
||||
└── Assets.xcassets # Nơi chứa LaunchScreen, AppIcon của iOS
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 2. Chuẩn Bị Các Tập Tin Cấu Hình Cốt Lõi
|
||||
|
||||
### 🔹 File `2.1. .gitignore` (Đặt tại thư mục gốc)
|
||||
|
||||
Tập tin này giúp bỏ qua các file rác của hệ thống Linux, Android Studio và Xcode khi push code lên Debian Git Server.
|
||||
|
||||
```ini
|
||||
# --- OS Files ---
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.swp
|
||||
|
||||
# --- Android Studio / Gradle ---
|
||||
.gradle/
|
||||
build/
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
local.properties
|
||||
*.apk
|
||||
*.aar
|
||||
*.bms
|
||||
.idea/workspace.xml
|
||||
.idea/libraries/
|
||||
.idea/caches/
|
||||
|
||||
# --- Xcode / iOS ---
|
||||
build/
|
||||
DerivedData/
|
||||
*.xcodeproj/*
|
||||
!*.xcodeproj/project.pbxproj
|
||||
!*.xcodeproj/xcshareddata/
|
||||
*.xcworkspace/
|
||||
!*.xcworkspace/contents.xcworkspacedata
|
||||
.strongspace/
|
||||
project.xcworkspace/
|
||||
xcuserdata/
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprint
|
||||
|
||||
```
|
||||
|
||||
### 🔹 File `2.2. AndroidManifest.xml` (Cấu hình quyền Android)
|
||||
|
||||
Đặt tại `android-project/app/src/main/AndroidManifest.xml`. Cần khai báo sẵn các quyền về Camera, Lưu trữ và In ấn.
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.photobooth.app">
|
||||
|
||||
<!-- Quyền truy cập Camera -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||
|
||||
<!-- Quyền lưu ảnh (Android 10 trở xuống cần WRITE, Android 11+ dùng Scoped Storage) -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- Quyền Internet để in ấn qua mạng Local -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Retro Photobooth"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
```
|
||||
|
||||
### 🔹 File `2.3. Info.plist` (Cấu hình quyền iOS)
|
||||
|
||||
Đặt sơ bộ tại `ios-project/PhotoboothApp/Info.plist` để khi chuyển sang Mac build không bị crash do thiếu giải trình quyền.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.photobooth.app</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Retro Photobooth</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
|
||||
<!-- Giải trình quyền sử dụng Camera -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Ứng dụng cần sử dụng Camera để chụp ảnh Photobooth.</string>
|
||||
|
||||
<!-- Giải trình quyền lưu trữ ảnh vào Thư viện -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Ứng dụng cần quyền lưu ảnh đã chụp và trang trí vào thư viện của bạn.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Ứng dụng cần quyền thêm ảnh vào thư viện của bạn.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏃♂️ 3. Các Lệnh Triển Khai Nhanh Trên Debian Terminal
|
||||
|
||||
Để tạo nhanh bộ khung này trên Debian mà không cần tạo thủ công từng thư mục, bạn có thể chạy đoạn script ngắn này:
|
||||
|
||||
```bash
|
||||
# 1. Tạo thư mục gốc dự án
|
||||
mkdir -p photobooth-project && cd photobooth-project
|
||||
|
||||
# 2. Tạo cấu trúc thư mục tài nguyên và các project con
|
||||
mkdir -p assets/fonts assets/frames assets/stickers
|
||||
mkdir -p android-project/app/src/main/java/com/photobooth/app
|
||||
mkdir -p ios-project/PhotoboothApp/Assets.xcassets
|
||||
|
||||
# 3. Khởi tạo Git repository nội bộ
|
||||
git init
|
||||
|
||||
---
|
||||
|
||||
## 📅 4. Kế Hoạch Chạy Thử Nghiệm Giai Đoạn 1 (Sprint 1 - Tuần 1-2)
|
||||
|
||||
* [ ] **Chuẩn bị dữ liệu mẫu:** Copy ít nhất 2 font `.ttf`, 2 khung hình `.png` và 3 sticker vào các thư mục tương ứng trong `assets/`.
|
||||
* [ ] **Mở Android Studio trên Debian:** Import thư mục `android-project`.
|
||||
* [ ] **Cấu hình Gradle:** Tích hợp các dependency cho CameraX (`androidx.camera:camera-camera2`).
|
||||
* [ ] **Code kiểm thử:** Tạo màn hình Preview Camera cơ bản trên Android Emulator (đã bật KVM) để đảm bảo luồng hình ảnh phần cứng hoạt động ổn định.
|
||||
|
||||
> 💡 **Mẹo nhỏ:** Khi viết code xử lý tọa độ kéo thả sticker trên Android sau này, hãy thiết kế một cấu trúc dữ liệu kiểu JSON đại diện cho "vị trí tương đối" (tính theo %) để sau này đưa sang iOS đọc hiểu y hệt:
|
||||
> ```json
|
||||
> { "sticker_id": "sunglasses", "x_percent": 0.25, "y_percent": 0.40, "scale": 1.2, "rotation_degree": 45 }
|
||||
>
|
||||
> ```
|
||||
@@ -0,0 +1,25 @@
|
||||
# Retro Photobooth (Vintage Style)
|
||||
|
||||
Dự án ứng dụng Photobooth di động đa nền tảng (Native iOS/Android) với phong cách vintage cổ điển. Dự án được cấu trúc theo dạng Monorepo để quản lý chung toàn bộ tài nguyên và mã nguồn của cả hai nền tảng.
|
||||
|
||||
## 📁 Cấu Trúc Thư Mục (Monorepo)
|
||||
|
||||
- `assets/`: Tài nguyên dùng chung (fonts, frames, stickers).
|
||||
- `android-project/`: Mã nguồn dự án Native Android (sử dụng Kotlin, Jetpack Compose, CameraX).
|
||||
- `ios-project/`: Mã nguồn dự án Native iOS (Swift, Xcode).
|
||||
|
||||
## 🚀 Hướng Dẫn Bắt Đầu Nhanh
|
||||
|
||||
### Môi trường Android
|
||||
1. Mở Android Studio và chọn **Open An Existing Project**.
|
||||
2. Tìm đến thư mục `android-project/` và import dự án.
|
||||
3. Chờ Gradle sync hoàn tất.
|
||||
4. Chạy ứng dụng trên Thiết bị mô phỏng (Emulator) hoặc Thiết bị thật hỗ trợ API 24 trở lên.
|
||||
|
||||
### Môi trường iOS
|
||||
1. Mở Xcode trên macOS.
|
||||
2. Mở file `ios-project/PhotoboothApp.xcodeproj` hoặc thư mục dự án tương ứng.
|
||||
3. Đảm bảo đã khai báo các quyền truy cập camera và thư viện ảnh trong `Info.plist`.
|
||||
|
||||
## 📜 Giấy Phép & Tài Liệu Tham Khảo
|
||||
Chi tiết kế hoạch triển khai và roadmap có thể xem tại tệp `PLAN.md`.
|
||||
@@ -0,0 +1,77 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.photobooth.app"
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.photobooth.app"
|
||||
minSdk = 24
|
||||
targetSdk = 33
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.2")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.06.01"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
|
||||
// CameraX dependencies for Sprint 1 Camera preview
|
||||
val cameraVersion = "1.3.0-rc01"
|
||||
implementation("androidx.camera:camera-core:$cameraVersion")
|
||||
implementation("androidx.camera:camera-camera2:$cameraVersion")
|
||||
implementation("androidx.camera:camera-lifecycle:$cameraVersion")
|
||||
implementation("androidx.camera:camera-view:$cameraVersion")
|
||||
implementation("androidx.camera:camera-extensions:$cameraVersion")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.06.01"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.photobooth.app">
|
||||
|
||||
<!-- Quyền truy cập Camera -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||
|
||||
<!-- Quyền lưu ảnh (Android 10 trở xuống cần WRITE, Android 11+ dùng Scoped Storage) -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- Quyền Internet để in ấn qua mạng Local -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Retro Photobooth"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.photobooth.app
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState: Bundle?)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
Greeting("Retro Photobooth")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Welcome to $name!",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
MaterialTheme {
|
||||
Greeting("Retro Photobooth")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.1.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Retro Photobooth"
|
||||
include(":app")
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 598 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 494 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 473 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 685 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 699 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.photobooth.app</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Retro Photobooth</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
|
||||
<!-- Giải trình quyền sử dụng Camera -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Ứng dụng cần sử dụng Camera để chụp ảnh Photobooth.</string>
|
||||
|
||||
<!-- Giải trình quyền lưu trữ ảnh vào Thư viện -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Ứng dụng cần quyền lưu ảnh đã chụp và trang trí vào thư viện của bạn.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Ứng dụng cần quyền thêm ảnh vào thư viện của bạn.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user