# Техническое задание и промпт для создания 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 ) 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 ``` ### 📚 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 { val cameras = mutableListOf() 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!**