From 40e016e128aeafb4df9346d8c93669b0ba9ffd9c Mon Sep 17 00:00:00 2001 From: "Andrey K. Choi" Date: Sun, 28 Sep 2025 22:11:39 +0900 Subject: [PATCH] android app --- .../9.0-milestone-1/checksums/checksums.lock | Bin 0 -> 17 bytes .../checksums/md5-checksums.bin | Bin 0 -> 25897 bytes .../checksums/sha1-checksums.bin | Bin 0 -> 42689 bytes .../executionHistory/executionHistory.bin | Bin 0 -> 19675 bytes .../executionHistory/executionHistory.lock | Bin 0 -> 17 bytes .../fileChanges/last-build.bin | Bin 0 -> 1 bytes .../9.0-milestone-1/fileHashes/fileHashes.bin | Bin 0 -> 18697 bytes .../fileHashes/fileHashes.lock | Bin 0 -> 17 bytes .../.gradle/9.0-milestone-1/gc.properties | 0 .../buildOutputCleanup.lock | Bin 0 -> 17 bytes .../buildOutputCleanup/cache.properties | 2 + .../buildOutputCleanup/outputFiles.bin | Bin 0 -> 18695 bytes android-client/.gradle/config.properties | 2 + android-client/.gradle/vcs-1/gc.properties | 0 android-client/.run/app.run.xml | 9 + android-client/android-client.iml | 11 - android-client/app/build.gradle | 68 ++ android-client/app/proguard-rules.pro | 29 + .../app/src/main/AndroidManifest.xml | 41 ++ .../java/com/godeye/android/MainActivity.kt | 256 +++++++ .../res/drawable/ic_launcher_foreground.xml | 13 + .../app/src/main/res/layout/activity_main.xml | 92 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 164 bytes .../app/src/main/res/values/colors.xml | 17 + .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 14 + .../app/src/main/res/values/themes.xml | 18 + .../app/src/main/res/xml/backup_rules.xml | 6 + .../main/res/xml/data_extraction_rules.xml | 7 + android-client/build.gradle | 24 + .../reports/problems/problems-report.html | 663 ++++++++++++++++++ android-client/gradle.properties | 16 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android-client/gradlew | 184 +++++ android-client/gradlew.bat | 90 +++ android-client/local.properties | 8 + android-client/settings.gradle | 18 + backend/god-eye.log | 26 + 41 files changed, 1625 insertions(+), 11 deletions(-) create mode 100644 android-client/.gradle/9.0-milestone-1/checksums/checksums.lock create mode 100644 android-client/.gradle/9.0-milestone-1/checksums/md5-checksums.bin create mode 100644 android-client/.gradle/9.0-milestone-1/checksums/sha1-checksums.bin create mode 100644 android-client/.gradle/9.0-milestone-1/executionHistory/executionHistory.bin create mode 100644 android-client/.gradle/9.0-milestone-1/executionHistory/executionHistory.lock create mode 100644 android-client/.gradle/9.0-milestone-1/fileChanges/last-build.bin create mode 100644 android-client/.gradle/9.0-milestone-1/fileHashes/fileHashes.bin create mode 100644 android-client/.gradle/9.0-milestone-1/fileHashes/fileHashes.lock create mode 100644 android-client/.gradle/9.0-milestone-1/gc.properties create mode 100644 android-client/.gradle/buildOutputCleanup/buildOutputCleanup.lock create mode 100644 android-client/.gradle/buildOutputCleanup/cache.properties create mode 100644 android-client/.gradle/buildOutputCleanup/outputFiles.bin create mode 100644 android-client/.gradle/config.properties create mode 100644 android-client/.gradle/vcs-1/gc.properties create mode 100644 android-client/.run/app.run.xml delete mode 100644 android-client/android-client.iml create mode 100644 android-client/app/build.gradle create mode 100644 android-client/app/proguard-rules.pro create mode 100644 android-client/app/src/main/AndroidManifest.xml create mode 100644 android-client/app/src/main/java/com/godeye/android/MainActivity.kt create mode 100644 android-client/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 android-client/app/src/main/res/layout/activity_main.xml create mode 100644 android-client/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 android-client/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 android-client/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android-client/app/src/main/res/values/colors.xml create mode 100644 android-client/app/src/main/res/values/ic_launcher_background.xml create mode 100644 android-client/app/src/main/res/values/strings.xml create mode 100644 android-client/app/src/main/res/values/themes.xml create mode 100644 android-client/app/src/main/res/xml/backup_rules.xml create mode 100644 android-client/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 android-client/build.gradle create mode 100644 android-client/build/reports/problems/problems-report.html create mode 100644 android-client/gradle.properties create mode 100644 android-client/gradle/wrapper/gradle-wrapper.jar create mode 100644 android-client/gradle/wrapper/gradle-wrapper.properties create mode 100755 android-client/gradlew create mode 100644 android-client/gradlew.bat create mode 100644 android-client/local.properties create mode 100644 android-client/settings.gradle diff --git a/android-client/.gradle/9.0-milestone-1/checksums/checksums.lock b/android-client/.gradle/9.0-milestone-1/checksums/checksums.lock new file mode 100644 index 0000000000000000000000000000000000000000..46f889322cea4af41cd158ace16916299cd06e02 GIT binary patch literal 17 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 literal 0 HcmV?d00001 diff --git a/android-client/.gradle/9.0-milestone-1/checksums/sha1-checksums.bin b/android-client/.gradle/9.0-milestone-1/checksums/sha1-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..0e43c1728423961917e897a590b81f1628af4f35 GIT binary patch literal 42689 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 literal 0 HcmV?d00001 diff --git a/android-client/.gradle/9.0-milestone-1/executionHistory/executionHistory.bin b/android-client/.gradle/9.0-milestone-1/executionHistory/executionHistory.bin new file mode 100644 index 0000000000000000000000000000000000000000..1f4977460799f0e2e2c09d990d382b85f1195156 GIT binary patch literal 19675 zcmeI%Pe>F|90%~X>q2HmbLfv4(M74C-ErMr*FR7xYZmkm&5AC=+nJa1ba!TZGwYV* z$*3gpPX|$qqJt6z8H7>Pr3j*;gNNu4MFbUehzN>^vSxQ()aoGEA>unQGmm-i{eJKF z;k~_VgpeZX$Mi*Jt`##)v>*Th2tWV=5P$##AOHafKmY;|fB*y_009U<00I#BUj&++ zLJG3Q6m4{;8_QCJlodJ+SDCu{fP3lKxor2g+T8yKxwrFuL$(i32tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*#k z1%Ut|o&o3XPTwPKfePAYL`6_Uqjp^HpeAQ(MTe2%QuXW7%ST4fZ2mB1JbpVeIcEI! zg^Zh`#n&RJ7Gpk|;69sLZI&-3Xd=OcZ-4f!M3`y{YBNn*Q>u&?6)(QMr0~|qW8<5G z?gd96_gzX|lE<_rRq%wZlqe!;C~C~Kl7jh5a;Km19yLZ;-VxAS-EZF5Oy{WCoLQNKj~1M8CLoc*4U6P%&-(YlZ_hiw zPALb!W%8bIdrn)K)0RnjX}%?1_)pV05H@90{{R3 literal 0 HcmV?d00001 diff --git a/android-client/.gradle/9.0-milestone-1/fileChanges/last-build.bin b/android-client/.gradle/9.0-milestone-1/fileChanges/last-build.bin new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d GIT binary patch literal 1 IcmZPo000310RR91 literal 0 HcmV?d00001 diff --git a/android-client/.gradle/9.0-milestone-1/fileHashes/fileHashes.bin b/android-client/.gradle/9.0-milestone-1/fileHashes/fileHashes.bin new file mode 100644 index 0000000000000000000000000000000000000000..8efabc2c9f80e8d40ca71e6945a4adace18a3a7b GIT binary patch literal 18697 zcmeI&J4*vW6o%o61QiXKLS?ZL1;L1yNP-qAVnB&niLe&2P!KT)79oWorU)q{T4XCx zRKy~UKfp>0Eo?1Qh^fTF!p>V*FSE`pK~mbudthe|XZT>}X?MnjF#KP-M77$gEgb>~ zAbcfTQy!#A9meRdbDEZQA%Z^HcpqN_eMd97 zvf7?3y8jRCcXQwgX?N~3vYR_+7a=__x$}wWy%ieB&*^#0ox5*2PpLgt1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5crFLbE)6C@ZaS;{^8}-tAo~;%MGi&vtpjdgz)TJYx9e8Q&#h?Tn2w9PNF@;M1& literal 0 HcmV?d00001 diff --git a/android-client/.gradle/9.0-milestone-1/gc.properties b/android-client/.gradle/9.0-milestone-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-client/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android-client/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000000000000000000000000000000000000..d839cc4107b7b237bab2d43ee12ea7a6ffaa9fa8 GIT binary patch literal 17 TcmZRMP11j*a!Sd80Rs2{C`JQ1 literal 0 HcmV?d00001 diff --git a/android-client/.gradle/buildOutputCleanup/cache.properties b/android-client/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..91d7695 --- /dev/null +++ b/android-client/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Sun Sep 28 22:04:42 KST 2025 +gradle.version=9.0-milestone-1 diff --git a/android-client/.gradle/buildOutputCleanup/outputFiles.bin b/android-client/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000000000000000000000000000000000000..02e36e6eae71121c420220fb68fe8362c8cbd27f GIT binary patch literal 18695 zcmeI&F-yZh7=Ynx7SR@U5u$?~oFWQhrQoE3LN^Pg4laV?4{*xjQi@1B!`zYcj2qN!6lHQ0#j~y9bd;PJt)*fBGlpbim-EC%Py?P-1Li;jDXy1B1Yt&jN zap}j}^=eFPI9lV300IagfB*srAb + + + \ No newline at end of file diff --git a/android-client/android-client.iml b/android-client/android-client.iml deleted file mode 100644 index f76df2b..0000000 --- a/android-client/android-client.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android-client/app/build.gradle b/android-client/app/build.gradle new file mode 100644 index 0000000..fd50cec --- /dev/null +++ b/android-client/app/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.godeye.android' + compileSdk 34 + + defaultConfig { + applicationId "com.godeye.android" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled 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 { + viewBinding true + buildConfig true + } +} + +dependencies { + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' + + // WebRTC + implementation 'org.webrtc:google-webrtc:1.0.32006' + + // Socket.IO + implementation 'io.socket:socket.io-client:2.1.0' + + // Camera + implementation 'androidx.camera:camera-core:1.3.0' + implementation 'androidx.camera:camera-camera2:1.3.0' + implementation 'androidx.camera:camera-lifecycle:1.3.0' + implementation 'androidx.camera:camera-view:1.3.0' + + // JSON + implementation 'com.google.code.gson:gson:2.10.1' + + // Testing + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} diff --git a/android-client/app/proguard-rules.pro b/android-client/app/proguard-rules.pro new file mode 100644 index 0000000..93183c1 --- /dev/null +++ b/android-client/app/proguard-rules.pro @@ -0,0 +1,29 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# WebRTC ProGuard rules +-keep class org.webrtc.** { *; } +-dontwarn org.webrtc.** + +# Socket.IO ProGuard rules +-keep class io.socket.** { *; } +-dontwarn io.socket.** diff --git a/android-client/app/src/main/AndroidManifest.xml b/android-client/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a016f17 --- /dev/null +++ b/android-client/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android-client/app/src/main/java/com/godeye/android/MainActivity.kt b/android-client/app/src/main/java/com/godeye/android/MainActivity.kt new file mode 100644 index 0000000..506b422 --- /dev/null +++ b/android-client/app/src/main/java/com/godeye/android/MainActivity.kt @@ -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, 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() + } +} \ No newline at end of file diff --git a/android-client/app/src/main/res/drawable/ic_launcher_foreground.xml b/android-client/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..3626962 --- /dev/null +++ b/android-client/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/android-client/app/src/main/res/layout/activity_main.xml b/android-client/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..ca64a44 --- /dev/null +++ b/android-client/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + +