diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 184028e..6d7aba8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -90,9 +90,6 @@ - diff --git a/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt b/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt index 9c43023..f34a6fb 100644 --- a/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt +++ b/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt @@ -45,14 +45,7 @@ object OfflineCmdServiceHelper { ) private val CMDS_SPRAYING = listOf( OfflineCmdBean("开始", "kai shi"), - OfflineCmdBean("开始任务", "kai shi ren wu"), - OfflineCmdBean("拍照", "pai zhao"), - OfflineCmdBean("拍摄", "pai she"), - OfflineCmdBean("放大", "fang da"), - OfflineCmdBean("拉近", "la jin"), - OfflineCmdBean("缩小", "suo xiao"), - OfflineCmdBean("拉远", "la yuan"), - OfflineCmdBean("取消", "qu xiao") + OfflineCmdBean("开始任务", "kai shi ren wu") ) private val CMDS_SPRAYING_FINISH = listOf( OfflineCmdBean("补充照片", "bu chong zhao pian"), diff --git a/app/src/main/java/com/nova/brain/glass/ui/CameraPreviewPocActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/CameraPreviewPocActivity.kt deleted file mode 100644 index c5a3ff6..0000000 --- a/app/src/main/java/com/nova/brain/glass/ui/CameraPreviewPocActivity.kt +++ /dev/null @@ -1,407 +0,0 @@ -package com.nova.brain.glass.ui - -import android.Manifest -import android.content.Intent -import android.content.pm.PackageManager -import android.graphics.ImageFormat -import android.graphics.Rect -import android.graphics.SurfaceTexture -import android.hardware.camera2.CameraCaptureSession -import android.hardware.camera2.CameraCharacteristics -import android.hardware.camera2.CameraAccessException -import android.hardware.camera2.CameraDevice -import android.hardware.camera2.CameraManager -import android.hardware.camera2.CaptureRequest -import android.hardware.camera2.TotalCaptureResult -import android.hardware.camera2.params.StreamConfigurationMap -import android.media.ImageReader -import android.os.Bundle -import android.os.Environment -import android.os.Handler -import android.os.HandlerThread -import android.view.KeyEvent -import android.view.Surface -import android.view.TextureView -import androidx.core.app.ActivityCompat -import com.nova.brain.glass.R -import com.nova.brain.glass.databinding.ActivityCameraPreviewPocBinding -import com.nova.brain.glass.helper.OfflineCmdListener -import com.nova.brain.glass.helper.OfflineCmdServiceHelper -import com.xuqm.base.extensions.showMessage -import com.xuqm.base.ui.BaseActivity -import java.io.File -import java.io.FileOutputStream -import java.util.UUID -import kotlin.math.roundToInt - -class CameraPreviewPocActivity : BaseActivity() { - - companion object { - const val EXTRA_PHOTO_PATH = "extra_photo_path" - private const val REQUEST_CAMERA_PERMISSION = 1001 - } - - override fun getLayoutId(): Int = R.layout.activity_camera_preview_poc - override fun fullscreen(): Boolean = true - - private val cameraManager by lazy { getSystemService(CameraManager::class.java) } - private var cameraDevice: CameraDevice? = null - private var captureSession: CameraCaptureSession? = null - private var previewRequestBuilder: CaptureRequest.Builder? = null - private var imageReader: ImageReader? = null - private var backgroundThread: HandlerThread? = null - private var backgroundHandler: Handler? = null - private var activeArraySize: Rect? = null - private var maxZoom = 1f - private var zoomLevel = 1f - private var currentOutputPath: String? = null - private var isCapturing = false - - private val offlineCmdListener = object : OfflineCmdListener { - override fun onOfflineCmd(cmd: String) { - runOnUiThread { - when (cmd) { - "退出", "返回", "退回", "取消" -> finish() - "拍照", "拍摄", "开始", "开始任务" -> captureStillImage() - "放大", "拉近" -> adjustZoom(0.2f) - "缩小", "拉远" -> adjustZoom(-0.2f) - } - } - } - } - - private val textureListener = object : TextureView.SurfaceTextureListener { - override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { - openCamera() - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) = Unit - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = true - override fun onSurfaceTextureUpdated(surface: SurfaceTexture) = Unit - } - - override fun initView(savedInstanceState: Bundle?) { - super.initView(savedInstanceState) - binding.previewView.surfaceTextureListener = textureListener - binding.captureButton.setOnClickListener { captureStillImage() } - binding.zoomInButton.setOnClickListener { adjustZoom(0.2f) } - binding.zoomOutButton.setOnClickListener { adjustZoom(-0.2f) } - updateZoomText() - } - - override fun onResume() { - super.onResume() - startBackgroundThread() - OfflineCmdServiceHelper.addOnLineListener(offlineCmdListener) - OfflineCmdServiceHelper.addListenerSpraying() - if (binding.previewView.isAvailable) { - openCamera() - } else { - binding.previewView.surfaceTextureListener = textureListener - } - } - - override fun onPause() { - closeCamera() - OfflineCmdServiceHelper.removeListenerSpraying() - OfflineCmdServiceHelper.removeOnLineListener(offlineCmdListener) - stopBackgroundThread() - super.onPause() - } - - override fun dispatchKeyEvent(event: KeyEvent): Boolean { - if (event.action != KeyEvent.ACTION_DOWN) return super.dispatchKeyEvent(event) - return when (event.keyCode) { - KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_VOLUME_DOWN -> { - adjustZoom(-0.2f) - true - } - KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_EQUALS, KeyEvent.KEYCODE_VOLUME_UP -> { - adjustZoom(0.2f) - true - } - KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_CAMERA -> { - captureStillImage() - true - } - else -> super.dispatchKeyEvent(event) - } - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode != REQUEST_CAMERA_PERMISSION) return - if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { - openCamera() - } else { - "缺少相机权限,无法验证预览".showMessage() - finish() - } - } - - private fun openCamera() { - if ( - ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED - ) { - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), - REQUEST_CAMERA_PERMISSION - ) - return - } - try { - val selectedCameraId = selectBackCameraId() ?: run { - "未找到可用相机".showMessage() - finish() - return - } - val characteristics = cameraManager.getCameraCharacteristics(selectedCameraId) - activeArraySize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE) - maxZoom = (characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: 1f) - .coerceAtLeast(1f) - zoomLevel = zoomLevel.coerceIn(1f, maxZoom) - val streamMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) - val previewSize = choosePreviewSize(streamMap) - val captureSize = chooseCaptureSize(streamMap) - val texture = binding.previewView.surfaceTexture ?: return - texture.setDefaultBufferSize(previewSize.width, previewSize.height) - imageReader?.close() - imageReader = ImageReader.newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 2).apply { - setOnImageAvailableListener({ reader -> - val image = reader.acquireNextImage() ?: return@setOnImageAvailableListener - val outputPath = currentOutputPath - if (outputPath == null) { - image.close() - return@setOnImageAvailableListener - } - val buffer = image.planes.firstOrNull()?.buffer - val bytes = ByteArray(buffer?.remaining() ?: 0) - buffer?.get(bytes) - image.close() - runCatching { - FileOutputStream(outputPath).use { it.write(bytes) } - }.onSuccess { - runOnUiThread { - setResult(RESULT_OK, Intent().putExtra(EXTRA_PHOTO_PATH, outputPath)) - finish() - } - }.onFailure { - isCapturing = false - runOnUiThread { - binding.statusText.text = "图片保存失败: ${it.message ?: "未知错误"}" - } - } - }, backgroundHandler) - } - binding.statusText.text = "相机预览验证中,可拉近拉远后拍照" - cameraManager.openCamera(selectedCameraId, object : CameraDevice.StateCallback() { - override fun onOpened(device: CameraDevice) { - cameraDevice = device - createPreviewSession(texture) - } - - override fun onDisconnected(device: CameraDevice) { - device.close() - cameraDevice = null - runOnUiThread { binding.statusText.text = "相机已断开" } - } - - override fun onError(device: CameraDevice, error: Int) { - device.close() - cameraDevice = null - runOnUiThread { binding.statusText.text = "相机打开失败: $error" } - } - }, backgroundHandler) - } catch (securityException: SecurityException) { - binding.statusText.text = "缺少相机权限" - "缺少相机权限,无法打开预览".showMessage() - } - } - - private fun createPreviewSession(texture: SurfaceTexture) { - val device = cameraDevice ?: return - val previewSurface = Surface(texture) - previewRequestBuilder = try { - device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply { - addTarget(previewSurface) - set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) - set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) - applyZoom(this) - } - } catch (e: CameraAccessException) { - handleCameraFailure("相机预览初始化失败: ${e.message}") - return - } catch (e: IllegalStateException) { - handleCameraFailure("相机设备不可用: ${e.message}") - return - } - val readerSurface = imageReader?.surface ?: return - try { - device.createCaptureSession( - listOf(previewSurface, readerSurface), - object : CameraCaptureSession.StateCallback() { - override fun onConfigured(session: CameraCaptureSession) { - captureSession = session - val request = previewRequestBuilder?.build() ?: return - try { - session.setRepeatingRequest(request, null, backgroundHandler) - } catch (e: CameraAccessException) { - handleCameraFailure("相机预览启动失败: ${e.message}") - } catch (e: IllegalStateException) { - handleCameraFailure("相机会话不可用: ${e.message}") - } - } - - override fun onConfigureFailed(session: CameraCaptureSession) { - handleCameraFailure("相机预览配置失败") - } - }, - backgroundHandler - ) - } catch (e: CameraAccessException) { - handleCameraFailure("相机会话创建失败: ${e.message}") - } catch (e: IllegalStateException) { - handleCameraFailure("相机会话不可用: ${e.message}") - } - } - - private fun captureStillImage() { - if (isCapturing) return - val device = cameraDevice ?: return - val session = captureSession ?: return - val readerSurface = imageReader?.surface ?: return - val outputPath = createOutputFile().absolutePath - currentOutputPath = outputPath - isCapturing = true - binding.statusText.text = "拍照中,请保持稳定..." - try { - val captureBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply { - addTarget(readerSurface) - set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) - set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) - applyZoom(this) - set(CaptureRequest.JPEG_ORIENTATION, 0) - } - session.stopRepeating() - session.capture(captureBuilder.build(), object : CameraCaptureSession.CaptureCallback() { - override fun onCaptureCompleted( - session: CameraCaptureSession, - request: CaptureRequest, - result: TotalCaptureResult - ) { - previewRequestBuilder?.build()?.let { - runCatching { - session.setRepeatingRequest(it, null, backgroundHandler) - } - } - } - }, backgroundHandler) - } catch (e: CameraAccessException) { - isCapturing = false - handleCameraFailure("拍照失败: ${e.message}") - } catch (e: IllegalStateException) { - isCapturing = false - handleCameraFailure("相机设备不可用: ${e.message}") - } - } - - private fun adjustZoom(delta: Float) { - maxZoom = maxZoom.coerceAtLeast(1f) - zoomLevel = (zoomLevel + delta).coerceIn(1f, maxZoom) - updateZoomText() - val requestBuilder = previewRequestBuilder ?: return - val session = captureSession ?: return - applyZoom(requestBuilder) - try { - session.setRepeatingRequest(requestBuilder.build(), null, backgroundHandler) - } catch (e: CameraAccessException) { - handleCameraFailure("缩放失败: ${e.message}") - } catch (e: IllegalStateException) { - handleCameraFailure("相机会话不可用: ${e.message}") - } - } - - private fun updateZoomText() { - binding.zoomText.text = "缩放 ${String.format("%.1f", zoomLevel)}x" - } - - private fun applyZoom(builder: CaptureRequest.Builder) { - val sensorRect = activeArraySize ?: return - if (zoomLevel <= 1f) { - builder.set(CaptureRequest.SCALER_CROP_REGION, sensorRect) - return - } - val centerX = sensorRect.centerX() - val centerY = sensorRect.centerY() - val deltaX = (0.5f * sensorRect.width() / zoomLevel).roundToInt() - val deltaY = (0.5f * sensorRect.height() / zoomLevel).roundToInt() - val cropRect = Rect(centerX - deltaX, centerY - deltaY, centerX + deltaX, centerY + deltaY) - builder.set(CaptureRequest.SCALER_CROP_REGION, cropRect) - } - - private fun selectBackCameraId(): String? { - val ids = cameraManager.cameraIdList ?: return null - return ids.firstOrNull { id -> - val facing = cameraManager.getCameraCharacteristics(id) - .get(CameraCharacteristics.LENS_FACING) - facing == CameraCharacteristics.LENS_FACING_BACK - } ?: ids.firstOrNull() - } - - private fun choosePreviewSize(map: StreamConfigurationMap?): android.util.Size { - val sizes = map?.getOutputSizes(SurfaceTexture::class.java).orEmpty() - return sizes.maxByOrNull { it.width * it.height } ?: android.util.Size(1920, 1080) - } - - private fun chooseCaptureSize(map: StreamConfigurationMap?): android.util.Size { - val sizes = map?.getOutputSizes(ImageFormat.JPEG).orEmpty() - return sizes.maxByOrNull { it.width * it.height } ?: android.util.Size(1920, 1080) - } - - private fun createOutputFile(): File { - val publicPicturesDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - if (!publicPicturesDir.exists()) { - publicPicturesDir.mkdirs() - } - return File(publicPicturesDir, "camera_poc_${UUID.randomUUID()}.jpg") - } - private fun closeCamera() { - captureSession?.close() - captureSession = null - cameraDevice?.close() - cameraDevice = null - imageReader?.close() - imageReader = null - isCapturing = false - currentOutputPath = null - } - - private fun handleCameraFailure(message: String) { - closeCamera() - runOnUiThread { - binding.statusText.text = message - message.showMessage() - setResult(RESULT_CANCELED) - finish() - } - } - - private fun startBackgroundThread() { - if (backgroundThread != null) return - backgroundThread = HandlerThread("camera-preview-poc").also { it.start() } - backgroundHandler = Handler(backgroundThread!!.looper) - } - - private fun stopBackgroundThread() { - backgroundThread?.quitSafely() - backgroundThread?.join() - backgroundThread = null - backgroundHandler = null - } -} diff --git a/app/src/main/java/com/nova/brain/glass/ui/SprayingActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/SprayingActivity.kt index 9d06660..45b2aee 100644 --- a/app/src/main/java/com/nova/brain/glass/ui/SprayingActivity.kt +++ b/app/src/main/java/com/nova/brain/glass/ui/SprayingActivity.kt @@ -1,19 +1,26 @@ package com.nova.brain.glass.ui import android.content.Intent +import android.os.Environment import androidx.recyclerview.widget.RecyclerView import com.nova.brain.glass.R import com.nova.brain.glass.databinding.ActivitySprayingBinding +import com.nova.brain.glass.helper.GlassMediaServiceHelper import com.nova.brain.glass.helper.OfflineCmdListener import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.SprayingPhotoManager import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.viewmodel.SprayingVM +import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution +import com.rokid.security.system.server.media.callback.PhotoFileCallback import com.xuqm.base.adapter.BasePagedAdapter import com.xuqm.base.adapter.CommonPagedAdapter import com.xuqm.base.adapter.ViewHolder +import com.xuqm.base.common.LogHelper import com.xuqm.base.extensions.showMessage import com.xuqm.base.ui.BaseListFormLayoutNormalActivity +import java.io.File +import java.util.UUID class SprayingActivity : BaseListFormLayoutNormalActivity() { @@ -28,10 +35,6 @@ class SprayingActivity : } private var productionInfoId: String = "" - companion object { - private const val REQUEST_CAMERA_PREVIEW = 2001 - } - private val listener = object : OfflineCmdListener { override fun onOfflineCmd(cmd: String) { runOnUiThread { @@ -45,6 +48,7 @@ class SprayingActivity : binding.hint.text = "拍照中,请稍后..." } SprayingPhotoManager.clear() + isPhoto = true takePhoto() } } @@ -54,10 +58,46 @@ class SprayingActivity : } fun takePhoto() { - startActivityForResult( - Intent(this, CameraPreviewPocActivity::class.java), - REQUEST_CAMERA_PREVIEW - ) + val fileName = "test_${System.currentTimeMillis()}.png" + val publicPicturesDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + val file = File(publicPicturesDir, fileName) + GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_1080P, file.absolutePath) + } + + private val photoCallbackId = UUID.randomUUID().toString() + + private val mPhotoFileCallback = object : PhotoFileCallback.Stub() { + override fun onTakePhoto(path: String) { + LogHelper.d("onTakePhoto-->path = $path") + } + + override fun getCallbackId(): String { + return photoCallbackId + } + + override fun onTakePhotoV2(path: String?, width: Int, height: Int) { + LogHelper.d("width:$width--height:$height") + if (path == null) { + if (isPhoto) { + isPhoto = false + takePhoto() + } else { + runOnUiThread { + binding.hint.text = "单击或语音输入“开始”,进入下一步" + } + "相机异常".showMessage() + } + } else { + SprayingPhotoManager.addPhoto(path) + startActivity(Intent(this@SprayingActivity, SprayingOCRActivity::class.java).apply { + putExtra("path", path) + putExtra("taskId", taskId) + putExtra("productionInfoId", productionInfoId) + }) + finish() + } + } } @@ -83,6 +123,7 @@ class SprayingActivity : override fun onResume() { super.onResume() + GlassMediaServiceHelper.addPhotoCallback(mPhotoFileCallback) OfflineCmdServiceHelper.addOnLineListener(listener) OfflineCmdServiceHelper.addListenerSpraying() } @@ -91,28 +132,7 @@ class SprayingActivity : super.onPause() OfflineCmdServiceHelper.removeListenerSpraying() OfflineCmdServiceHelper.removeOnLineListener(listener) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode != REQUEST_CAMERA_PREVIEW) return - if (resultCode != RESULT_OK) { - binding.hint.text = "单击或语音输入“开始”,进入下一步" - return - } - val path = data?.getStringExtra(CameraPreviewPocActivity.EXTRA_PHOTO_PATH) - if (path.isNullOrBlank()) { - binding.hint.text = "单击或语音输入“开始”,进入下一步" - "未获取到照片".showMessage() - return - } - SprayingPhotoManager.addPhoto(path) - startActivity(Intent(this, SprayingOCRActivity::class.java).apply { - putExtra("path", path) - putExtra("taskId", taskId) - putExtra("productionInfoId", productionInfoId) - }) - finish() + GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback) } override fun onDestroy() { @@ -120,6 +140,7 @@ class SprayingActivity : window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } + private var isPhoto = false private val adapter = object : CommonPagedAdapter(R.layout.item_photo) { override fun convert(holder: ViewHolder, item: ItemItem, position: Int) { holder @@ -130,6 +151,7 @@ class SprayingActivity : binding.hint.text = "拍照中,请稍后..." } SprayingPhotoManager.clear() + isPhoto = true takePhoto() } } diff --git a/app/src/main/res/layout/activity_camera_preview_poc.xml b/app/src/main/res/layout/activity_camera_preview_poc.xml deleted file mode 100644 index 108e875..0000000 --- a/app/src/main/res/layout/activity_camera_preview_poc.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -