init commit
This commit is contained in:
11
android-client/android-client.iml
Normal file
11
android-client/android-client.iml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
256
android-client/src/main/java/com/godeye/android/MainActivity.kt
Normal file
256
android-client/src/main/java/com/godeye/android/MainActivity.kt
Normal file
@@ -0,0 +1,256 @@
|
||||
package com.godeye.android
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.godeye.android.camera.CameraManager
|
||||
import com.godeye.android.databinding.ActivityMainBinding
|
||||
import com.godeye.android.network.SocketManager
|
||||
import com.godeye.android.webrtc.WebRTCManager
|
||||
import org.webrtc.EglBase
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var socketManager: SocketManager
|
||||
private lateinit var cameraManager: CameraManager
|
||||
private lateinit var webRTCManager: WebRTCManager
|
||||
private lateinit var eglBase: EglBase
|
||||
|
||||
private val deviceId by lazy {
|
||||
Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PERMISSION_REQUEST_CODE = 100
|
||||
private val REQUIRED_PERMISSIONS = arrayOf(
|
||||
Manifest.permission.CAMERA,
|
||||
Manifest.permission.RECORD_AUDIO,
|
||||
Manifest.permission.INTERNET,
|
||||
Manifest.permission.ACCESS_NETWORK_STATE
|
||||
)
|
||||
|
||||
private const val SERVER_URL = "http://10.0.2.2:3000" // Для эмулятора, для реального устройства укажите IP сервера
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
val missingPermissions = REQUIRED_PERMISSIONS.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
if (missingPermissions.isNotEmpty()) {
|
||||
ActivityCompat.requestPermissions(this, missingPermissions.toTypedArray(), PERMISSION_REQUEST_CODE)
|
||||
} else {
|
||||
initializeComponents()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
||||
if (requestCode == PERMISSION_REQUEST_CODE) {
|
||||
val allPermissionsGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
|
||||
|
||||
if (allPermissionsGranted) {
|
||||
initializeComponents()
|
||||
} else {
|
||||
showPermissionDeniedDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPermissionDeniedDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("Необходимы разрешения")
|
||||
.setMessage("Для работы приложения необходим доступ к камере, микрофону и интернету")
|
||||
.setPositiveButton("OK") { _, _ -> finish() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun initializeComponents() {
|
||||
try {
|
||||
// Инициализируем EGL контекст для WebRTC
|
||||
eglBase = EglBase.create()
|
||||
|
||||
// Инициализируем менеджеры
|
||||
cameraManager = CameraManager(this, eglBase)
|
||||
webRTCManager = WebRTCManager(this, cameraManager)
|
||||
socketManager = SocketManager(SERVER_URL)
|
||||
|
||||
setupSocketCallbacks()
|
||||
setupWebRTCCallbacks()
|
||||
setupUI()
|
||||
connectToServer()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("MainActivity", "Error initializing components", e)
|
||||
Toast.makeText(this, "Ошибка инициализации: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSocketCallbacks() {
|
||||
socketManager.onCameraRequest = { sessionId, operatorId, cameraTypeStr ->
|
||||
runOnUiThread {
|
||||
val cameraType = parseCameraType(cameraTypeStr)
|
||||
showCameraRequestDialog(sessionId, operatorId, cameraType)
|
||||
}
|
||||
}
|
||||
|
||||
socketManager.onCameraSwitch = { sessionId, cameraTypeStr ->
|
||||
runOnUiThread {
|
||||
val cameraType = parseCameraType(cameraTypeStr)
|
||||
val success = webRTCManager.switchCamera(sessionId, cameraType)
|
||||
updateCameraStatus(sessionId, if (success) "Камера переключена на: $cameraTypeStr" else "Ошибка переключения камеры")
|
||||
}
|
||||
}
|
||||
|
||||
socketManager.onCameraDisconnect = { sessionId ->
|
||||
runOnUiThread {
|
||||
webRTCManager.closeSession(sessionId)
|
||||
updateConnectionStatus("Оператор отключился")
|
||||
}
|
||||
}
|
||||
|
||||
socketManager.onWebRTCAnswer = { sessionId, answer ->
|
||||
webRTCManager.handleAnswer(sessionId, answer)
|
||||
}
|
||||
|
||||
socketManager.onWebRTCIceCandidate = { sessionId, candidate ->
|
||||
webRTCManager.handleICECandidate(sessionId, candidate)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWebRTCCallbacks() {
|
||||
webRTCManager.onOfferCreated = { sessionId, offer ->
|
||||
socketManager.sendWebRTCOffer(sessionId, offer)
|
||||
}
|
||||
|
||||
webRTCManager.onAnswerCreated = { sessionId, answer ->
|
||||
socketManager.sendWebRTCAnswer(sessionId, answer)
|
||||
}
|
||||
|
||||
webRTCManager.onICECandidate = { sessionId, candidate ->
|
||||
socketManager.sendICECandidate(sessionId, candidate)
|
||||
}
|
||||
|
||||
webRTCManager.onConnectionStateChanged = { sessionId, state ->
|
||||
runOnUiThread {
|
||||
val statusText = when (state) {
|
||||
org.webrtc.PeerConnection.PeerConnectionState.CONNECTING -> "Подключение..."
|
||||
org.webrtc.PeerConnection.PeerConnectionState.CONNECTED -> "Подключено"
|
||||
org.webrtc.PeerConnection.PeerConnectionState.DISCONNECTED -> "Отключено"
|
||||
org.webrtc.PeerConnection.PeerConnectionState.FAILED -> "Ошибка подключения"
|
||||
org.webrtc.PeerConnection.PeerConnectionState.CLOSED -> "Соединение закрыто"
|
||||
else -> "Неизвестно"
|
||||
}
|
||||
updateConnectionStatus("$statusText (Сессия: $sessionId)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
// Показываем доступные типы камер
|
||||
val availableTypes = cameraManager.getAvailableCameraTypes()
|
||||
binding.availableCamerasText.text = "Доступные камеры: ${availableTypes.joinToString(", ")}"
|
||||
|
||||
// Показываем Device ID
|
||||
binding.deviceIdText.text = "Device ID: $deviceId"
|
||||
|
||||
// Статус подключения
|
||||
updateConnectionStatus("Инициализация...")
|
||||
}
|
||||
|
||||
private fun connectToServer() {
|
||||
val deviceInfo = mapOf(
|
||||
"model" to android.os.Build.MODEL,
|
||||
"manufacturer" to android.os.Build.MANUFACTURER,
|
||||
"androidVersion" to android.os.Build.VERSION.RELEASE,
|
||||
"appVersion" to BuildConfig.VERSION_NAME
|
||||
)
|
||||
|
||||
socketManager.connect(deviceId, deviceInfo)
|
||||
updateConnectionStatus("Подключение к серверу...")
|
||||
}
|
||||
|
||||
private fun handleCameraRequest(sessionId: String, operatorId: String, cameraType: String) {
|
||||
runOnUiThread {
|
||||
binding.tvStatus.text = "Запрос доступа к камере от оператора: $operatorId"
|
||||
binding.tvCameraType.text = "Тип камеры: $cameraType"
|
||||
|
||||
try {
|
||||
cameraManager.startCamera(cameraType) { success ->
|
||||
if (success) {
|
||||
val streamUrl = webRTCManager.createOffer(sessionId) { offer ->
|
||||
socketManager.sendWebRTCOffer(sessionId, offer)
|
||||
}
|
||||
socketManager.respondToCameraRequest(sessionId, true, streamUrl)
|
||||
updateConnectionStatus("Трансляция активна")
|
||||
} else {
|
||||
socketManager.respondToCameraRequest(sessionId, false, null, "Не удалось запустить камеру")
|
||||
updateConnectionStatus("Ошибка запуска камеры")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("MainActivity", "Error starting camera", e)
|
||||
socketManager.respondToCameraRequest(sessionId, false, null, e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCameraSwitch(sessionId: String, cameraType: String) {
|
||||
runOnUiThread {
|
||||
binding.tvCameraType.text = "Переключение на: $cameraType"
|
||||
cameraManager.switchCamera(cameraType) { success ->
|
||||
if (success) {
|
||||
updateConnectionStatus("Камера переключена на $cameraType")
|
||||
} else {
|
||||
updateConnectionStatus("Ошибка переключения камеры")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCameraDisconnect(sessionId: String) {
|
||||
runOnUiThread {
|
||||
cameraManager.stopCamera()
|
||||
webRTCManager.endSession(sessionId)
|
||||
updateConnectionStatus("Трансляция завершена")
|
||||
binding.tvCameraType.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateConnectionStatus(status: String) {
|
||||
runOnUiThread {
|
||||
binding.tvStatus.text = status
|
||||
Log.i("MainActivity", "Status: $status")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
binding.btnDisconnect.setOnClickListener {
|
||||
socketManager.disconnect()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cameraManager.release()
|
||||
webRTCManager.release()
|
||||
socketManager.disconnect()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user