Add SimpleCameraActivity for crash debugging - minimal camera with only preview and capture
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SimpleCameraActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -49,6 +49,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
private lateinit var photoPreviewLayout: LinearLayout
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private lateinit var cameraExecutor: ExecutorService
|
||||
private var cameraProvider: ProcessCameraProvider? = null
|
||||
|
||||
// 存储拍摄的图片URI
|
||||
private val capturedImageUris = mutableListOf<Uri>()
|
||||
@@ -65,6 +66,9 @@ class CameraActivity : AppCompatActivity() {
|
||||
settingsButton = findViewById(R.id.settingsButton)
|
||||
photoPreviewLayout = findViewById(R.id.photoPreviewLayout)
|
||||
|
||||
// 初始化线程池
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
// 请求权限
|
||||
if (allPermissionsGranted()) {
|
||||
startCamera()
|
||||
@@ -77,9 +81,12 @@ class CameraActivity : AppCompatActivity() {
|
||||
// 设置点击监听器
|
||||
captureButton.setOnClickListener { takePhoto() }
|
||||
settingsButton.setOnClickListener { openSettings() }
|
||||
}
|
||||
|
||||
// 初始化线程池
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cameraExecutor.shutdown()
|
||||
cameraProvider?.unbindAll()
|
||||
}
|
||||
|
||||
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
|
||||
@@ -90,35 +97,39 @@ class CameraActivity : AppCompatActivity() {
|
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
|
||||
cameraProviderFuture.addListener({
|
||||
val cameraProvider = cameraProviderFuture.get()
|
||||
|
||||
// 预览
|
||||
val preview = Preview.Builder()
|
||||
.build()
|
||||
.also {
|
||||
it.setSurfaceProvider(viewFinder.surfaceProvider)
|
||||
}
|
||||
|
||||
imageCapture = ImageCapture.Builder()
|
||||
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||||
.build()
|
||||
|
||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
|
||||
try {
|
||||
cameraProvider.unbindAll()
|
||||
cameraProvider.bindToLifecycle(
|
||||
cameraProvider = cameraProviderFuture.get()
|
||||
|
||||
// 预览
|
||||
val preview = Preview.Builder()
|
||||
.build()
|
||||
.also {
|
||||
it.setSurfaceProvider(viewFinder.surfaceProvider)
|
||||
}
|
||||
|
||||
imageCapture = ImageCapture.Builder()
|
||||
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||||
.build()
|
||||
|
||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
|
||||
cameraProvider?.unbindAll()
|
||||
cameraProvider?.bindToLifecycle(
|
||||
this, cameraSelector, preview, imageCapture
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "相机绑定失败", e)
|
||||
Log.e(TAG, "相机初始化失败: ${e.message}", e)
|
||||
Toast.makeText(this, "相机启动失败: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(this))
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun takePhoto() {
|
||||
val imageCapture = this.imageCapture ?: return
|
||||
val imageCapture = this.imageCapture ?: run {
|
||||
Toast.makeText(this, "相机未准备好", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val outputOptions = createOutputFileOptions()
|
||||
|
||||
@@ -143,7 +154,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Log.e(TAG, "拍照失败: ${exception.message}", exception)
|
||||
Toast.makeText(baseContext, "拍照失败", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(baseContext, "拍照失败: ${exception.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -308,8 +319,9 @@ class CameraActivity : AppCompatActivity() {
|
||||
if (allPermissionsGranted()) {
|
||||
startCamera()
|
||||
} else {
|
||||
Toast.makeText(this, "权限被拒绝,相机功能不可用", Toast.LENGTH_SHORT).show()
|
||||
// 可以在这里提供降级功能,比如手动输入位置信息
|
||||
Toast.makeText(this, "相机权限被拒绝,应用无法使用相机功能", Toast.LENGTH_LONG).show()
|
||||
// 可以选择关闭应用或提供替代功能
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// 直接启动相机Activity
|
||||
startActivity(Intent(this, CameraActivity::class.java))
|
||||
// 启动极简相机Activity
|
||||
startActivity(Intent(this, SimpleCameraActivity::class.java))
|
||||
finish() // 关闭MainActivity,避免返回键回到空白页面
|
||||
}
|
||||
}
|
||||
170
app/src/main/java/com/example/app/SimpleCameraActivity.kt
Normal file
170
app/src/main/java/com/example/app/SimpleCameraActivity.kt
Normal file
@@ -0,0 +1,170 @@
|
||||
package com.example.app
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentValues
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class SimpleCameraActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SimpleCamera"
|
||||
private const val REQUEST_CODE_PERMISSIONS = 10
|
||||
private val REQUIRED_PERMISSIONS = arrayOf(
|
||||
Manifest.permission.CAMERA
|
||||
)
|
||||
}
|
||||
|
||||
private lateinit var viewFinder: PreviewView
|
||||
private lateinit var captureButton: FloatingActionButton
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private lateinit var cameraExecutor: ExecutorService
|
||||
private var cameraProvider: ProcessCameraProvider? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_simple_camera)
|
||||
|
||||
// 初始化视图
|
||||
viewFinder = findViewById(R.id.viewFinder)
|
||||
captureButton = findViewById(R.id.captureButton)
|
||||
|
||||
// 初始化线程池
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
// 请求权限
|
||||
if (allPermissionsGranted()) {
|
||||
startCamera()
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(
|
||||
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
|
||||
)
|
||||
}
|
||||
|
||||
// 设置拍照按钮点击监听器
|
||||
captureButton.setOnClickListener { takePhoto() }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cameraExecutor.shutdown()
|
||||
cameraProvider?.unbindAll()
|
||||
}
|
||||
|
||||
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
|
||||
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
private fun startCamera() {
|
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
|
||||
cameraProviderFuture.addListener({
|
||||
try {
|
||||
cameraProvider = cameraProviderFuture.get()
|
||||
|
||||
// 预览
|
||||
val preview = Preview.Builder()
|
||||
.build()
|
||||
.also {
|
||||
it.setSurfaceProvider(viewFinder.surfaceProvider)
|
||||
}
|
||||
|
||||
imageCapture = ImageCapture.Builder()
|
||||
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||||
.build()
|
||||
|
||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
|
||||
cameraProvider?.unbindAll()
|
||||
cameraProvider?.bindToLifecycle(
|
||||
this, cameraSelector, preview, imageCapture
|
||||
)
|
||||
|
||||
Log.d(TAG, "相机启动成功")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "相机初始化失败: ${e.message}", e)
|
||||
Toast.makeText(this, "相机启动失败: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(this))
|
||||
}
|
||||
|
||||
private fun takePhoto() {
|
||||
val imageCapture = this.imageCapture ?: run {
|
||||
Toast.makeText(this, "相机未准备好", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
||||
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, "巡检_$timestamp")
|
||||
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/LogCam")
|
||||
}
|
||||
}
|
||||
|
||||
val contentResolver = contentResolver
|
||||
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
|
||||
if (uri == null) {
|
||||
Toast.makeText(this, "创建文件失败", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver, uri, contentValues).build()
|
||||
|
||||
imageCapture.takePicture(
|
||||
outputOptions,
|
||||
ContextCompat.getMainExecutor(this),
|
||||
object : ImageCapture.OnImageSavedCallback {
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
val savedUri = outputFileResults.savedUri ?: return
|
||||
val msg = "照片已保存: ${savedUri.lastPathSegment}"
|
||||
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
|
||||
Log.d(TAG, msg)
|
||||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Log.e(TAG, "拍照失败: ${exception.message}", exception)
|
||||
Toast.makeText(baseContext, "拍照失败: ${exception.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == REQUEST_CODE_PERMISSIONS) {
|
||||
if (allPermissionsGranted()) {
|
||||
startCamera()
|
||||
} else {
|
||||
Toast.makeText(this, "相机权限被拒绝,应用无法使用相机功能", Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
app/src/main/res/layout/activity_simple_camera.xml
Normal file
46
app/src/main/res/layout/activity_simple_camera.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".SimpleCameraActivity">
|
||||
|
||||
<!-- 相机预览视图 -->
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/viewFinder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- 拍照按钮 -->
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/captureButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:src="@android:drawable/ic_menu_camera"
|
||||
app:backgroundTint="@color/primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<!-- 状态提示 -->
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="极简相机 - 点击下方按钮拍照"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:background="#80000000"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -7,4 +7,5 @@
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="primary">#FF6200EE</color>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user