Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save Camera Properties to external file when in debug mode #261

Merged
merged 14 commits into from
Sep 11, 2024
2 changes: 1 addition & 1 deletion app/src/main/java/com/google/jetpackcamera/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class MainActivity : ComponentActivity() {
}

private val isDebugMode: Boolean
get() = intent?.getBooleanExtra(KEY_DEBUG_MODE, false) ?: false
get() = true

private fun getStandardMode(): PreviewMode.StandardMode {
return PreviewMode.StandardMode { event ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ interface CameraUseCase {
*
* @return list of available lenses.
*/
suspend fun initialize(cameraAppSettings: CameraAppSettings, useCaseMode: UseCaseMode)
suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: UseCaseMode,
isDebugMode: Boolean = false
)

/**
* Starts the camera.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ import android.Manifest
import android.app.Application
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.TotalCaptureResult
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.Environment.DIRECTORY_DOCUMENTS
import android.os.SystemClock
import android.provider.MediaStore
import android.util.Log
Expand Down Expand Up @@ -74,6 +77,8 @@ import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.lifecycle.asFlow
import com.google.jetpackcamera.core.camera.CameraUseCase.ScreenFlashEvent.Type
import com.google.jetpackcamera.core.camera.DebugCameraInfoUtil.getAllCamerasPropertiesJSONArray
import com.google.jetpackcamera.core.camera.DebugCameraInfoUtil.writeFileExternalStorage
import com.google.jetpackcamera.core.camera.effects.SingleSurfaceForcingEffect
import com.google.jetpackcamera.settings.SettableConstraintsRepository
import com.google.jetpackcamera.settings.model.AspectRatio
Expand Down Expand Up @@ -105,6 +110,7 @@ import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.Channel
Expand All @@ -124,6 +130,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private const val TAG = "CameraXCameraUseCase"
const val TARGET_FPS_AUTO = 0
Expand Down Expand Up @@ -170,6 +177,8 @@ constructor(
if (!physicalCameraId.equals(this.physicalCameraId) ||
logicalCameraId != this.logicalCameraId
) {
this.physicalCameraId = physicalCameraId
this.logicalCameraId = logicalCameraId
_currentCameraState.update { old ->
old.copy(
debugInfo = DebugInfo(logicalCameraId, physicalCameraId)
Expand Down Expand Up @@ -213,7 +222,8 @@ constructor(

override suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: CameraUseCase.UseCaseMode
useCaseMode: CameraUseCase.UseCaseMode,
isDebugMode: Boolean
) {
this.useCaseMode = useCaseMode
cameraProvider = ProcessCameraProvider.awaitInstance(application)
Expand Down Expand Up @@ -284,6 +294,19 @@ constructor(
.tryApplyImageFormatConstraints()
.tryApplyFrameRateConstraints()
.tryApplyStabilizationConstraints()
if (isDebugMode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
withContext(Dispatchers.IO) {
davidjiagoogle marked this conversation as resolved.
Show resolved Hide resolved
val cameraManager = application.baseContext.getSystemService(Context.CAMERA_SERVICE)
as CameraManager
val cameraProperties = getAllCamerasPropertiesJSONArray(cameraManager).toString()
val file = File(
Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS),
"JCACameraProperties"
davidjiagoogle marked this conversation as resolved.
Show resolved Hide resolved
)
writeFileExternalStorage(file, cameraProperties)
Log.d(TAG, "JCACameraProperties written to ${file.path}. \n$cameraProperties")
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.jetpackcamera.core.camera

import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.Environment
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import org.json.JSONArray
import org.json.JSONObject

private const val TAG = "DebugCameraInfoUtil"
object DebugCameraInfoUtil {
@RequiresApi(Build.VERSION_CODES.P)
fun getAllCamerasPropertiesJSONArray(cameraManager: CameraManager): JSONArray {
val result = JSONArray()
for (logicalCameraId in cameraManager.cameraIdList) {
val logicalCameraData = JSONObject()
logicalCameraData.put(
"logical-$logicalCameraId",
getCameraPropertiesJSONObject(logicalCameraId, cameraManager)
)
for (
physicalCameraId in
cameraManager.getCameraCharacteristics(logicalCameraId).physicalCameraIds
) {
logicalCameraData.put(
"physical-$physicalCameraId",
getCameraPropertiesJSONObject(physicalCameraId, cameraManager)
)
}
result.put(logicalCameraData)
}
return result
}

private fun getCameraPropertiesJSONObject(
cameraId: String,
cameraManager: CameraManager
): JSONObject {
val cameraCharacteristics =
cameraManager.getCameraCharacteristics(cameraId)
val jsonObject = JSONObject()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraCharacteristics.get(CameraCharacteristics.LENS_POSE_ROTATION)
?.let { jsonObject.put(CameraCharacteristics.LENS_POSE_ROTATION.name, it) }
cameraCharacteristics.get(CameraCharacteristics.LENS_POSE_TRANSLATION)
?.let { jsonObject.put(CameraCharacteristics.LENS_POSE_TRANSLATION.name, it) }
cameraCharacteristics.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION)
?.let { jsonObject.put(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION.name, it) }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
cameraCharacteristics.get(CameraCharacteristics.LENS_DISTORTION)
?.let { jsonObject.put(CameraCharacteristics.LENS_DISTORTION.name, it) }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
?.let { jsonObject.put(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE.name, it) }
}
cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
?.let {
jsonObject.put(
CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS.name,
it
)
}
cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)
?.let {
jsonObject.put(
CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE.name,
it
)
}
cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
?.let { jsonObject.put(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES.name, it) }

return jsonObject
davidjiagoogle marked this conversation as resolved.
Show resolved Hide resolved
}

fun writeFileExternalStorage(file: File, textToWrite: String) {
// Checking the availability state of the External Storage.
val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED != state) {
// If it isn't mounted - we can't write into it.
return
}

var outputStream: FileOutputStream? = null
file.createNewFile()
outputStream = FileOutputStream(file, true)

FileOutputStream(file).use { outputStream ->
outputStream.write(textToWrite.toByteArray())
}
outputStream.close()
davidjiagoogle marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class FakeCameraUseCase(

override suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: CameraUseCase.UseCaseMode
useCaseMode: CameraUseCase.UseCaseMode,
isDebugMode: Boolean
) {
initialized = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ class PreviewViewModel @AssistedInject constructor(
private var initializationDeferred: Deferred<Unit> = viewModelScope.async {
cameraUseCase.initialize(
cameraAppSettings = settingsRepository.defaultCameraAppSettings.first(),
previewMode.toUseCaseMode()
previewMode.toUseCaseMode(),
isDebugMode
)
}

Expand Down
Loading