refactor(spraying): 替换相机预览实现为系统媒体服务
- 移除 CameraPreviewPocActivity 及其相关布局文件 - 集成 GlassMediaServiceHelper 替代原有的相机预览功能 - 使用 PhotoFileCallback 处理拍照回调逻辑 - 更新拍照流程以支持异步回调处理 - 删除旧的 onActivityResult 处理代码 - 修改离线命令配置移除相机操作相关指令
这个提交包含在:
父节点
8258d77f1f
当前提交
56da0f5899
@ -90,9 +90,6 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.InspectionCompleteActivity"
|
android:name=".ui.InspectionCompleteActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<activity
|
|
||||||
android:name=".ui.CameraPreviewPocActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SprayingActivity"
|
android:name=".ui.SprayingActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|||||||
@ -45,14 +45,7 @@ object OfflineCmdServiceHelper {
|
|||||||
)
|
)
|
||||||
private val CMDS_SPRAYING = listOf(
|
private val CMDS_SPRAYING = listOf(
|
||||||
OfflineCmdBean("开始", "kai shi"),
|
OfflineCmdBean("开始", "kai shi"),
|
||||||
OfflineCmdBean("开始任务", "kai shi ren wu"),
|
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")
|
|
||||||
)
|
)
|
||||||
private val CMDS_SPRAYING_FINISH = listOf(
|
private val CMDS_SPRAYING_FINISH = listOf(
|
||||||
OfflineCmdBean("补充照片", "bu chong zhao pian"),
|
OfflineCmdBean("补充照片", "bu chong zhao pian"),
|
||||||
|
|||||||
@ -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<ActivityCameraPreviewPocBinding>() {
|
|
||||||
|
|
||||||
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<out String>,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +1,26 @@
|
|||||||
package com.nova.brain.glass.ui
|
package com.nova.brain.glass.ui
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Environment
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.nova.brain.glass.R
|
import com.nova.brain.glass.R
|
||||||
import com.nova.brain.glass.databinding.ActivitySprayingBinding
|
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.OfflineCmdListener
|
||||||
import com.nova.brain.glass.helper.OfflineCmdServiceHelper
|
import com.nova.brain.glass.helper.OfflineCmdServiceHelper
|
||||||
import com.nova.brain.glass.helper.SprayingPhotoManager
|
import com.nova.brain.glass.helper.SprayingPhotoManager
|
||||||
import com.nova.brain.glass.model.ItemItem
|
import com.nova.brain.glass.model.ItemItem
|
||||||
import com.nova.brain.glass.viewmodel.SprayingVM
|
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.BasePagedAdapter
|
||||||
import com.xuqm.base.adapter.CommonPagedAdapter
|
import com.xuqm.base.adapter.CommonPagedAdapter
|
||||||
import com.xuqm.base.adapter.ViewHolder
|
import com.xuqm.base.adapter.ViewHolder
|
||||||
|
import com.xuqm.base.common.LogHelper
|
||||||
import com.xuqm.base.extensions.showMessage
|
import com.xuqm.base.extensions.showMessage
|
||||||
import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
|
import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
class SprayingActivity :
|
class SprayingActivity :
|
||||||
BaseListFormLayoutNormalActivity<ItemItem, SprayingVM, ActivitySprayingBinding>() {
|
BaseListFormLayoutNormalActivity<ItemItem, SprayingVM, ActivitySprayingBinding>() {
|
||||||
@ -28,10 +35,6 @@ class SprayingActivity :
|
|||||||
}
|
}
|
||||||
private var productionInfoId: String = ""
|
private var productionInfoId: String = ""
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val REQUEST_CAMERA_PREVIEW = 2001
|
|
||||||
}
|
|
||||||
|
|
||||||
private val listener = object : OfflineCmdListener {
|
private val listener = object : OfflineCmdListener {
|
||||||
override fun onOfflineCmd(cmd: String) {
|
override fun onOfflineCmd(cmd: String) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
@ -45,6 +48,7 @@ class SprayingActivity :
|
|||||||
binding.hint.text = "拍照中,请稍后..."
|
binding.hint.text = "拍照中,请稍后..."
|
||||||
}
|
}
|
||||||
SprayingPhotoManager.clear()
|
SprayingPhotoManager.clear()
|
||||||
|
isPhoto = true
|
||||||
takePhoto()
|
takePhoto()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,10 +58,46 @@ class SprayingActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun takePhoto() {
|
fun takePhoto() {
|
||||||
startActivityForResult(
|
val fileName = "test_${System.currentTimeMillis()}.png"
|
||||||
Intent(this, CameraPreviewPocActivity::class.java),
|
val publicPicturesDir =
|
||||||
REQUEST_CAMERA_PREVIEW
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
GlassMediaServiceHelper.addPhotoCallback(mPhotoFileCallback)
|
||||||
OfflineCmdServiceHelper.addOnLineListener(listener)
|
OfflineCmdServiceHelper.addOnLineListener(listener)
|
||||||
OfflineCmdServiceHelper.addListenerSpraying()
|
OfflineCmdServiceHelper.addListenerSpraying()
|
||||||
}
|
}
|
||||||
@ -91,28 +132,7 @@ class SprayingActivity :
|
|||||||
super.onPause()
|
super.onPause()
|
||||||
OfflineCmdServiceHelper.removeListenerSpraying()
|
OfflineCmdServiceHelper.removeListenerSpraying()
|
||||||
OfflineCmdServiceHelper.removeOnLineListener(listener)
|
OfflineCmdServiceHelper.removeOnLineListener(listener)
|
||||||
}
|
GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback)
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@ -120,6 +140,7 @@ class SprayingActivity :
|
|||||||
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isPhoto = false
|
||||||
private val adapter = object : CommonPagedAdapter<ItemItem>(R.layout.item_photo) {
|
private val adapter = object : CommonPagedAdapter<ItemItem>(R.layout.item_photo) {
|
||||||
override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
|
override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
|
||||||
holder
|
holder
|
||||||
@ -130,6 +151,7 @@ class SprayingActivity :
|
|||||||
binding.hint.text = "拍照中,请稍后..."
|
binding.hint.text = "拍照中,请稍后..."
|
||||||
}
|
}
|
||||||
SprayingPhotoManager.clear()
|
SprayingPhotoManager.clear()
|
||||||
|
isPhoto = true
|
||||||
takePhoto()
|
takePhoto()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@android:color/black">
|
|
||||||
|
|
||||||
<TextureView
|
|
||||||
android:id="@+id/previewView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:keepScreenOn="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/controlBar"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/statusText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:background="@drawable/bg_item"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
android:text="相机预览验证中"
|
|
||||||
android:textColor="#FF40FF5E"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/zoomText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:background="@drawable/bg_item"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
android:text="缩放 1.0x"
|
|
||||||
android:textColor="#FF40FF5E"
|
|
||||||
android:textSize="14sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/statusText" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/controlBar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:background="@drawable/bg_task_title_selected"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="10dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/zoomOutButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/bg_photo"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingVertical="8dp"
|
|
||||||
android:text="拉远"
|
|
||||||
android:textColor="#FF40FF5E"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/captureButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/bg_photo"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingVertical="8dp"
|
|
||||||
android:text="拍照"
|
|
||||||
android:textColor="#FF40FF5E"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/zoomInButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/bg_photo"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingVertical="8dp"
|
|
||||||
android:text="拉近"
|
|
||||||
android:textColor="#FF40FF5E"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
||||||
正在加载...
在新工单中引用
屏蔽一个用户