480 lines
20 KiB
Markdown
480 lines
20 KiB
Markdown
# Техническое задание и промпт для создания Android приложения GodEye Signal Center
|
||
|
||
## 🎯 ТЕХНИЧЕСКОЕ ЗАДАНИЕ
|
||
|
||
### Назначение системы
|
||
Создать Android приложение для системы удаленного доступа к камерам смартфона "GodEye Signal Center". Приложение должно предоставлять операторам доступ к камерам устройства через WebRTC с возможностью переключения между различными типами камер.
|
||
|
||
### Архитектура системы
|
||
```
|
||
[Android App] ←→ [WebSocket/Socket.IO] ←→ [Backend Server] ←→ [Desktop Operator]
|
||
←→ [WebRTC P2P Connection] ←→ [Desktop Operator]
|
||
```
|
||
|
||
### Основные компоненты
|
||
1. **MainActivity** - главный экран с управлением подключением
|
||
2. **SocketService** - сервис для WebSocket соединения с backend
|
||
3. **CameraManager** - управление камерами устройства (Camera2 API)
|
||
4. **WebRTCManager** - обработка WebRTC соединений для видеопотока
|
||
5. **PermissionManager** - управление разрешениями приложения
|
||
6. **SessionManager** - управление активными сессиями с операторами
|
||
|
||
### Функциональные требования
|
||
- ✅ Подключение к backend серверу по WebSocket (Socket.IO)
|
||
- ✅ Регистрация устройства с передачей характеристик
|
||
- ✅ Получение и обработка запросов доступа к камере от операторов
|
||
- ✅ Диалоги согласия пользователя на доступ к камере
|
||
- ✅ WebRTC соединение для передачи видеопотока
|
||
- ✅ Переключение между камерами: основная, фронтальная, широкоугольная, телеобъектив
|
||
- ✅ Отображение активных сессий и их управление
|
||
- ✅ Автоматическое переподключение при обрыве соединения
|
||
- ✅ Работа в фоне (Foreground Service)
|
||
- ✅ Уведомления о статусе подключения
|
||
|
||
### Технические требования
|
||
- **Платформа**: Android API 24+ (Android 7.0+)
|
||
- **Язык**: Kotlin
|
||
- **Архитектура**: MVVM с LiveData
|
||
- **Сеть**: Socket.IO для сигнализации, WebRTC для медиа
|
||
- **Камера**: Camera2 API для работы с камерами
|
||
- **UI**: Material Design 3
|
||
- **Разрешения**: CAMERA, RECORD_AUDIO, INTERNET, FOREGROUND_SERVICE
|
||
|
||
## 📋 ПОЛНЫЙ ПРОМПТ ДЛЯ COPILOT
|
||
|
||
---
|
||
|
||
**Создай полное Android приложение на Kotlin для системы GodEye Signal Center со следующими требованиями:**
|
||
|
||
### 🚀 ОСНОВНАЯ ЗАДАЧА
|
||
Разработать Android приложение, которое подключается к backend серверу, получает запросы от операторов на доступ к камере устройства и предоставляет видеопоток через WebRTC с возможностью переключения между камерами.
|
||
|
||
### 📱 СТРУКТУРА ПРОЕКТА
|
||
```
|
||
app/
|
||
├── src/main/
|
||
│ ├── java/com/godeye/android/
|
||
│ │ ├── MainActivity.kt
|
||
│ │ ├── services/
|
||
│ │ │ ├── SocketService.kt
|
||
│ │ │ └── CameraService.kt
|
||
│ │ ├── managers/
|
||
│ │ │ ├── CameraManager.kt
|
||
│ │ │ ├── WebRTCManager.kt
|
||
│ │ │ ├── SessionManager.kt
|
||
│ │ │ └── PermissionManager.kt
|
||
│ │ ├── models/
|
||
│ │ │ ├── DeviceInfo.kt
|
||
│ │ │ ├── CameraSession.kt
|
||
│ │ │ └── SocketEvents.kt
|
||
│ │ ├── ui/
|
||
│ │ │ ├── dialogs/
|
||
│ │ │ │ └── CameraRequestDialog.kt
|
||
│ │ │ └── adapters/
|
||
│ │ │ └── SessionAdapter.kt
|
||
│ │ └── utils/
|
||
│ │ ├── Constants.kt
|
||
│ │ └── Extensions.kt
|
||
│ ├── res/
|
||
│ │ ├── layout/
|
||
│ │ │ ├── activity_main.xml
|
||
│ │ │ ├── dialog_camera_request.xml
|
||
│ │ │ └── item_session.xml
|
||
│ │ ├── values/
|
||
│ │ │ ├── strings.xml
|
||
│ │ │ ├── colors.xml
|
||
│ │ │ └── themes.xml
|
||
│ │ └── drawable/
|
||
│ └── AndroidManifest.xml
|
||
└── build.gradle (app)
|
||
```
|
||
|
||
### 🔧 BACKEND API ENDPOINTS
|
||
|
||
#### WebSocket События (Socket.IO на порту 3001):
|
||
```kotlin
|
||
// Регистрация устройства
|
||
socket.emit("register:android", JsonObject().apply {
|
||
addProperty("deviceId", deviceId)
|
||
add("deviceInfo", JsonObject().apply {
|
||
addProperty("model", Build.MODEL)
|
||
addProperty("androidVersion", Build.VERSION.RELEASE)
|
||
addProperty("appVersion", "1.0.0")
|
||
add("availableCameras", JsonArray().apply {
|
||
add("back"); add("front"); add("wide"); add("telephoto")
|
||
})
|
||
})
|
||
})
|
||
|
||
// Обработка входящих событий
|
||
socket.on("register:success") { /* успешная регистрация */ }
|
||
socket.on("camera:request") { /* запрос доступа к камере */ }
|
||
socket.on("camera:disconnect") { /* завершение сессии */ }
|
||
socket.on("camera:switch") { /* переключение камеры */ }
|
||
socket.on("webrtc:offer") { /* WebRTC offer от оператора */ }
|
||
socket.on("webrtc:ice-candidate") { /* ICE candidates */ }
|
||
|
||
// Отправка ответов
|
||
socket.emit("camera:response", JsonObject().apply {
|
||
addProperty("sessionId", sessionId)
|
||
addProperty("accepted", true)
|
||
})
|
||
socket.emit("webrtc:answer", JsonObject().apply {
|
||
addProperty("sessionId", sessionId)
|
||
add("answer", /* RTCSessionDescription */)
|
||
})
|
||
```
|
||
|
||
### 🎨 UI/UX ТРЕБОВАНИЯ
|
||
|
||
#### MainActivity.xml:
|
||
```xml
|
||
<!-- Основной экран с полями: -->
|
||
- TextView: Device ID (автогенерируемый)
|
||
- EditText: Server URL (по умолчанию: http://192.168.1.100:3001)
|
||
- TextView: Connection Status
|
||
- Button: Connect/Disconnect
|
||
- RecyclerView: Active Sessions
|
||
- ProgressBar: Connection progress
|
||
- FloatingActionButton: Settings
|
||
```
|
||
|
||
#### CameraRequestDialog.xml:
|
||
```xml
|
||
<!-- Диалог запроса доступа: -->
|
||
- ImageView: Operator avatar/icon
|
||
- TextView: "Оператор {operatorId} запрашивает доступ к камере {cameraType}"
|
||
- TextView: Session ID
|
||
- Button: "Разрешить" / "Отклонить"
|
||
- CheckBox: "Запомнить для этого оператора"
|
||
```
|
||
|
||
### 💾 DATA MODELS
|
||
|
||
```kotlin
|
||
data class DeviceInfo(
|
||
val model: String,
|
||
val androidVersion: String,
|
||
val appVersion: String,
|
||
val availableCameras: List<String>
|
||
)
|
||
|
||
data class CameraSession(
|
||
val sessionId: String,
|
||
val operatorId: String,
|
||
val cameraType: String,
|
||
val startTime: Long,
|
||
var isActive: Boolean = true,
|
||
var webRTCConnected: Boolean = false
|
||
)
|
||
|
||
sealed class SocketEvent {
|
||
data class RegisterAndroid(val deviceId: String, val deviceInfo: DeviceInfo) : SocketEvent()
|
||
data class CameraRequest(val sessionId: String, val operatorId: String, val cameraType: String) : SocketEvent()
|
||
data class CameraResponse(val sessionId: String, val accepted: Boolean) : SocketEvent()
|
||
data class WebRTCOffer(val sessionId: String, val offer: String) : SocketEvent()
|
||
data class WebRTCAnswer(val sessionId: String, val answer: String) : SocketEvent()
|
||
}
|
||
```
|
||
|
||
### 🔐 РАЗРЕШЕНИЯ (AndroidManifest.xml):
|
||
```xml
|
||
<uses-permission android:name="android.permission.CAMERA" />
|
||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||
<uses-permission android:name="android.permission.INTERNET" />
|
||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
|
||
|
||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||
<uses-feature android:name="android.hardware.microphone" android:required="true" />
|
||
```
|
||
|
||
### 📚 DEPENDENCIES (build.gradle):
|
||
```gradle
|
||
dependencies {
|
||
implementation 'androidx.core:core-ktx:1.12.0'
|
||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||
implementation 'com.google.android.material:material:1.11.0'
|
||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
|
||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
|
||
implementation 'androidx.activity:activity-ktx:1.8.2'
|
||
|
||
// Socket.IO для WebSocket соединения
|
||
implementation 'io.socket:socket.io-client:2.1.0'
|
||
|
||
// WebRTC для видеопотока
|
||
implementation 'org.webrtc:google-webrtc:1.0.32006'
|
||
|
||
// Camera2 API
|
||
implementation 'androidx.camera:camera-core:1.3.1'
|
||
implementation 'androidx.camera:camera-camera2:1.3.1'
|
||
implementation 'androidx.camera:camera-lifecycle:1.3.1'
|
||
implementation 'androidx.camera:camera-view:1.3.1'
|
||
|
||
// JSON парсинг
|
||
implementation 'com.google.code.gson:gson:2.10.1'
|
||
|
||
// Корутины
|
||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
||
|
||
// RecyclerView
|
||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||
|
||
// Work Manager для фоновых задач
|
||
implementation 'androidx.work:work-runtime-ktx:2.9.0'
|
||
}
|
||
```
|
||
|
||
### 🏗️ ОСНОВНЫЕ КЛАССЫ
|
||
|
||
#### 1. MainActivity.kt - Основная логика:
|
||
```kotlin
|
||
class MainActivity : AppCompatActivity() {
|
||
private lateinit var binding: ActivityMainBinding
|
||
private lateinit var socketService: SocketService
|
||
private lateinit var sessionAdapter: SessionAdapter
|
||
private val viewModel: MainViewModel by viewModels()
|
||
|
||
override fun onCreate(savedInstanceState: Bundle?) {
|
||
// Инициализация UI
|
||
// Проверка разрешений
|
||
// Настройка RecyclerView для сессий
|
||
// Подключение к SocketService
|
||
}
|
||
|
||
private fun connectToServer() {
|
||
val serverUrl = binding.etServerUrl.text.toString()
|
||
socketService.connect(serverUrl)
|
||
}
|
||
|
||
private fun showCameraRequestDialog(request: CameraRequest) {
|
||
CameraRequestDialog.newInstance(request).show(supportFragmentManager, "camera_request")
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. SocketService.kt - WebSocket соединение:
|
||
```kotlin
|
||
class SocketService : Service() {
|
||
private lateinit var socket: Socket
|
||
private val binder = LocalBinder()
|
||
|
||
fun connect(serverUrl: String) {
|
||
socket = IO.socket(serverUrl)
|
||
socket.connect()
|
||
registerDevice()
|
||
setupEventListeners()
|
||
}
|
||
|
||
private fun registerDevice() {
|
||
val deviceInfo = DeviceInfo(
|
||
model = Build.MODEL,
|
||
androidVersion = Build.VERSION.RELEASE,
|
||
appVersion = BuildConfig.VERSION_NAME,
|
||
availableCameras = CameraManager.getAvailableCameraTypes()
|
||
)
|
||
socket.emit("register:android", gson.toJson(RegisterAndroid(deviceId, deviceInfo)))
|
||
}
|
||
|
||
private fun setupEventListeners() {
|
||
socket.on("camera:request") { args ->
|
||
val request = gson.fromJson(args[0].toString(), CameraRequest::class.java)
|
||
// Отправить broadcast для показа диалога
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. CameraManager.kt - Управление камерами:
|
||
```kotlin
|
||
class CameraManager(private val context: Context) {
|
||
private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as android.hardware.camera2.CameraManager
|
||
private var currentCameraId: String? = null
|
||
private var captureSession: CameraCaptureSession? = null
|
||
|
||
fun getAvailableCameraTypes(): List<String> {
|
||
val cameras = mutableListOf<String>()
|
||
try {
|
||
for (cameraId in cameraManager.cameraIdList) {
|
||
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
||
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
|
||
when (facing) {
|
||
CameraCharacteristics.LENS_FACING_BACK -> cameras.add("back")
|
||
CameraCharacteristics.LENS_FACING_FRONT -> cameras.add("front")
|
||
}
|
||
// Проверка на wide и telephoto
|
||
}
|
||
} catch (e: Exception) {
|
||
Log.e("CameraManager", "Error getting cameras", e)
|
||
}
|
||
return cameras
|
||
}
|
||
|
||
@SuppressLint("MissingPermission")
|
||
fun startCamera(cameraType: String, surface: Surface) {
|
||
val cameraId = getCameraIdForType(cameraType) ?: return
|
||
cameraManager.openCamera(cameraId, cameraStateCallback, null)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4. WebRTCManager.kt - WebRTC соединения:
|
||
```kotlin
|
||
class WebRTCManager(private val context: Context) {
|
||
private lateinit var peerConnectionFactory: PeerConnectionFactory
|
||
private var peerConnection: RTCPeerConnection? = null
|
||
private var localVideoSource: VideoSource? = null
|
||
|
||
fun initialize() {
|
||
val initializationOptions = PeerConnectionFactory.InitializationOptions.builder(context)
|
||
.setEnableInternalTracer(false)
|
||
.createInitializationOptions()
|
||
PeerConnectionFactory.initialize(initializationOptions)
|
||
|
||
val options = PeerConnectionFactory.Options()
|
||
peerConnectionFactory = PeerConnectionFactory.builder()
|
||
.setOptions(options)
|
||
.createPeerConnectionFactory()
|
||
}
|
||
|
||
fun createOffer(sessionId: String) {
|
||
val mediaConstraints = MediaConstraints()
|
||
peerConnection?.createOffer(object : SdpObserver {
|
||
override fun onCreateSuccess(sessionDescription: SessionDescription) {
|
||
// Отправить offer через WebSocket
|
||
}
|
||
}, mediaConstraints)
|
||
}
|
||
|
||
fun handleAnswer(sessionId: String, answer: SessionDescription) {
|
||
peerConnection?.setRemoteDescription(SimpleSdpObserver(), answer)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 🔄 WORKFLOW ПРИЛОЖЕНИЯ
|
||
|
||
1. **Запуск приложения:**
|
||
- Проверка разрешений (CAMERA, AUDIO, INTERNET)
|
||
- Генерация уникального Device ID
|
||
- Определение доступных камер устройства
|
||
- Инициализация WebRTC
|
||
|
||
2. **Подключение к серверу:**
|
||
- Подключение Socket.IO к указанному URL
|
||
- Регистрация устройства с отправкой характеристик
|
||
- Ожидание подтверждения регистрации
|
||
- Запуск Foreground Service для фоновой работы
|
||
|
||
3. **Обработка запроса камеры:**
|
||
- Получение события "camera:request" от сервера
|
||
- Показ диалога пользователю с информацией об операторе
|
||
- При согласии - отправка "camera:response" с accepted: true
|
||
- Инициализация WebRTC соединения
|
||
|
||
4. **WebRTC соединение:**
|
||
- Создание RTCPeerConnection
|
||
- Настройка локального видеопотока с указанной камеры
|
||
- Обмен offer/answer/ice-candidates с оператором
|
||
- Начало передачи видеопотока
|
||
|
||
5. **Управление сессией:**
|
||
- Отображение активных сессий в RecyclerView
|
||
- Обработка команд переключения камеры
|
||
- Завершение сессии по команде оператора или пользователя
|
||
- Очистка ресурсов WebRTC
|
||
|
||
### ⚙️ НАСТРОЙКИ И КОНФИГУРАЦИЯ
|
||
|
||
#### SharedPreferences ключи:
|
||
```kotlin
|
||
object PreferenceKeys {
|
||
const val SERVER_URL = "server_url"
|
||
const val DEVICE_ID = "device_id"
|
||
const val AUTO_ACCEPT_REQUESTS = "auto_accept_requests"
|
||
const val CAMERA_QUALITY = "camera_quality"
|
||
const val NOTIFICATION_ENABLED = "notification_enabled"
|
||
}
|
||
```
|
||
|
||
#### Настройки WebRTC:
|
||
```kotlin
|
||
val iceServers = listOf(
|
||
RTCIceServer.builder("stun:stun.l.google.com:19302").build(),
|
||
RTCIceServer.builder("stun:stun1.l.google.com:19302").build()
|
||
)
|
||
|
||
val rtcConfig = RTCConfiguration(iceServers).apply {
|
||
tcpCandidatePolicy = RTCConfiguration.TcpCandidatePolicy.DISABLED
|
||
bundlePolicy = RTCConfiguration.BundlePolicy.MAXBUNDLE
|
||
rtcpMuxPolicy = RTCConfiguration.RtcpMuxPolicy.REQUIRE
|
||
}
|
||
```
|
||
|
||
### 🐛 ОБРАБОТКА ОШИБОК
|
||
|
||
```kotlin
|
||
sealed class AppError {
|
||
object NetworkError : AppError()
|
||
object CameraPermissionDenied : AppError()
|
||
object CameraNotAvailable : AppError()
|
||
object WebRTCConnectionFailed : AppError()
|
||
data class SocketError(val message: String) : AppError()
|
||
data class UnknownError(val throwable: Throwable) : AppError()
|
||
}
|
||
|
||
class ErrorHandler {
|
||
fun handleError(error: AppError, context: Context) {
|
||
when (error) {
|
||
is AppError.NetworkError -> showNetworkError(context)
|
||
is AppError.CameraPermissionDenied -> showPermissionDialog(context)
|
||
is AppError.WebRTCConnectionFailed -> restartWebRTC()
|
||
// и т.д.
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 📋 ТЕСТИРОВАНИЕ
|
||
|
||
1. **Запуск backend сервера**: `cd backend && node src/server.js`
|
||
2. **Веб-демо оператора**: `http://localhost:3001`
|
||
3. **Подключение Android**: указать URL `http://192.168.1.100:3001`
|
||
4. **Тестовые сценарии**:
|
||
- Регистрация устройства
|
||
- Запрос доступа к камере от веб-демо
|
||
- Переключение между камерами
|
||
- Разрыв и восстановление соединения
|
||
|
||
### 🚀 ФИНАЛЬНЫЕ ТРЕБОВАНИЯ
|
||
|
||
**Создай полный рабочий проект со всеми перечисленными файлами, который:**
|
||
- ✅ Компилируется без ошибок на API 24+
|
||
- ✅ Корректно подключается к backend серверу
|
||
- ✅ Отображает запросы операторов в диалогах
|
||
- ✅ Передает видеопоток через WebRTC
|
||
- ✅ Поддерживает переключение камер
|
||
- ✅ Работает в фоне с уведомлениями
|
||
- ✅ Имеет современный Material Design UI
|
||
- ✅ Обрабатывает все ошибки и исключения
|
||
|
||
**Backend server URL для тестирования**: `http://localhost:3001` или `http://192.168.1.100:3001`
|
||
|
||
---
|
||
|
||
### 📝 ДОПОЛНИТЕЛЬНЫЕ ПРИМЕЧАНИЯ
|
||
|
||
- Используй современный Kotlin синтаксис с корутинами
|
||
- Применяй MVVM архитектуру с LiveData/StateFlow
|
||
- Все строки выноси в strings.xml с поддержкой русского языка
|
||
- Добавь логирование всех важных событий
|
||
- Используй Material Design 3 компоненты
|
||
- Обеспечь backward compatibility с API 24
|
||
- Добавь комментарии к сложной логике WebRTC и Camera2
|
||
|
||
**Этот промпт содержит все необходимые технические детали для создания полнофункционального Android приложения GodEye Signal Center!**
|