Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions app/src/main/java/app/grapheneos/camera/CamConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import app.grapheneos.camera.ui.showIgnoringShortEdgeMode
import app.grapheneos.camera.util.edit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.zxing.BarcodeFormat
import java.util.Collections
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors

Expand Down Expand Up @@ -111,6 +112,8 @@ class CamConfig(private val mActivity: MainActivity) {

const val ENABLE_ZSL = "enable_zsl"

const val OUTPUT_IMAGE_FORMAT = "output_image_format"

// const val IMAGE_FILE_FORMAT = "image_quality"
// const val VIDEO_FILE_FORMAT = "video_quality"
}
Expand Down Expand Up @@ -156,6 +159,8 @@ class CamConfig(private val mActivity: MainActivity) {

const val ENABLE_ZSL = false

const val OUTPUT_IMAGE_FORMAT = "JPEG"

// const val IMAGE_FILE_FORMAT = ""
// const val VIDEO_FILE_FORMAT = ""
}
Expand Down Expand Up @@ -195,6 +200,13 @@ class CamConfig(private val mActivity: MainActivity) {
val REAR_CAMERA_SELECTOR = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()

// Array of image formats currently supported by the camera app
private val supportedImageFormats = arrayOf(
ImageCapture.OUTPUT_FORMAT_RAW,
ImageCapture.OUTPUT_FORMAT_JPEG,
ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR,
)
}

var camera: Camera? = null
Expand Down Expand Up @@ -555,6 +567,40 @@ class CamConfig(private val mActivity: MainActivity) {
editor.apply()
}

var outputImageFormat : Int
get() {
return mActivity.settingsDialog.titleToImageFormat(
modePref.getString(
outputImageFormatKey,
SettingValues.Default.OUTPUT_IMAGE_FORMAT
)!!
)
}
set(format) {
val formatTitle = mActivity.settingsDialog.getTitleForImageFormat(format)

val editor = modePref.edit()
editor.putString(outputImageFormatKey, formatTitle)
editor.apply()
}

private val outputImageFormatKey: String
get() {
val pf = if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
"FRONT"
} else {
"BACK"
}

return "${SettingValues.Key.OUTPUT_IMAGE_FORMAT}_$pf"
}

fun getAvailableImageFormats() : Set<Int> {
val cameraInfo = camera?.cameraInfo ?: return Collections.emptySet()
return ImageCapture.getImageCaptureCapabilities(cameraInfo)
.supportedOutputFormats.filter { it in supportedImageFormats }.toSet()
}

val isZslSupported : Boolean by lazy {
camera!!.cameraInfo.isZslSupported
}
Expand Down Expand Up @@ -710,9 +756,7 @@ class CamConfig(private val mActivity: MainActivity) {
putBoolean(SettingValues.Key.GEO_TAGGING, SettingValues.Default.GEO_TAGGING)
}

if (isVideoMode) {
mActivity.settingsDialog.reloadQualities()
}
mActivity.settingsDialog.reloadSettings()

if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
if (!modePref.contains(SettingValues.Key.SELF_ILLUMINATION)) {
Expand Down Expand Up @@ -1193,6 +1237,8 @@ class CamConfig(private val mActivity: MainActivity) {
it.setJpegQuality(photoQuality)
}

it.setOutputFormat(outputImageFormat)

it.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class ImageSaver(
cropRect = if (ImageUtil.shouldCropImage(image)) image.cropRect else null
val imageFormat = image.format

origJpegBytes = if (imageFormat == ImageFormat.JPEG) {
origJpegBytes = if (imageFormat == ImageFormat.JPEG || imageFormat == ImageFormat.JPEG_R) {
ImageUtil.jpegImageToJpegByteArray(image)
} else if (imageFormat == ImageFormat.YUV_420_888) {
ImageUtil.yuvImageToJpegByteArray(image, cropRect, jpegQuality, 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class VideoCapturer(private val mActivity: MainActivity) {
animator.start()

mActivity.settingsDialog.videoQualitySpinner.isEnabled = false
mActivity.settingsDialog.imageFormatSpinner.isEnabled = false
mActivity.settingsDialog.enableEISToggle.isEnabled = false

mActivity.flipCamIcon.setImageResource(R.drawable.pause)
Expand Down Expand Up @@ -351,6 +352,7 @@ class VideoCapturer(private val mActivity: MainActivity) {
mActivity.flipCamIcon.setImageResource(R.drawable.flip_camera)

mActivity.settingsDialog.videoQualitySpinner.isEnabled = true
mActivity.settingsDialog.imageFormatSpinner.isEnabled = true
mActivity.settingsDialog.enableEISToggle.isEnabled = true

if (mActivity !is VideoCaptureActivity) {
Expand Down
108 changes: 104 additions & 4 deletions app/src/main/java/app/grapheneos/camera/ui/SettingsDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
private var gridToggle: ImageView
var videoQualitySpinner: Spinner
private lateinit var vQAdapter: ArrayAdapter<String>

var imageFormatSpinner: Spinner
private lateinit var imageFormatAdapter: ArrayAdapter<String>

private var focusTimeoutSpinner: Spinner
private var timerSpinner: Spinner

Expand All @@ -84,6 +88,7 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
private var enableEISSetting: View
private var selfIlluminationSetting: View
private var videoQualitySetting: View
private var imageFormatSetting: View
private var timerSetting: View

var settingsFrame: View
Expand Down Expand Up @@ -226,19 +231,36 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
videoQualitySpinner.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
p0: AdapterView<*>?,
p1: View?,
parent: AdapterView<*>?,
view: View?,
position: Int,
p3: Long
id: Long
) {

val choice = vQAdapter.getItem(position) as String
updateVideoQuality(choice)
}

override fun onNothingSelected(p0: AdapterView<*>?) {}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}

imageFormatSpinner = binding.imageFormatSpinner

imageFormatSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val choice = imageFormatAdapter.getItem(position) as String
updateOutputImageFormat(choice)
}

override fun onNothingSelected(parent: AdapterView<*>?) {}

}

qRadio = binding.qualityRadio
lRadio = binding.latencyRadio

Expand Down Expand Up @@ -333,6 +355,7 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
enableEISSetting = binding.enableEisSetting
selfIlluminationSetting = binding.selfIlluminationSetting
videoQualitySetting = binding.videoQualitySetting
imageFormatSetting = binding.imageFormatSetting
timerSetting = binding.timerSetting

includeAudioToggle = binding.includeAudioSwitch
Expand Down Expand Up @@ -515,6 +538,21 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
}
}

fun updateOutputImageFormat(choice: String, resCam: Boolean = true) {

val imageFormat = titleToImageFormat(choice)

if (imageFormat == camConfig.outputImageFormat) return

camConfig.outputImageFormat = imageFormat

if (resCam) {
camConfig.startCamera(true)
} else {
imageFormatSpinner.setSelection(getAvailableQTitles().indexOf(choice))
}
}

fun titleToQuality(title: String): Quality {
return when (title) {
"2160p (UHD)" -> Quality.UHD
Expand Down Expand Up @@ -740,6 +778,40 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
}
}

private fun getAvailableImageFormatTitles(): List<String> {
val titles = arrayListOf<String>()

camConfig.getAvailableImageFormats().forEach { format ->
titles.add(getTitleForImageFormat(format))
}

return titles
}

fun getTitleForImageFormat(format: Int): String {
return when (format) {
ImageCapture.OUTPUT_FORMAT_RAW -> "DNG"
ImageCapture.OUTPUT_FORMAT_JPEG -> "JPEG"
ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR -> "JPEG (UHD)"
else -> {
Log.e("TAG", "Unknown image format constant: $format")
"Unknown"
}
}
}

fun titleToImageFormat(title: String): Int {
return when (title) {
"DNG" -> ImageCapture.OUTPUT_FORMAT_RAW
"JPEG" -> ImageCapture.OUTPUT_FORMAT_JPEG
"JPEG (UHD)" -> ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR
else -> {
Log.e("TAG", "Unknown title for image format: $title")
ImageCapture.OUTPUT_FORMAT_JPEG
}
}
}

fun updateGridToggleUI() {
mActivity.previewGrid.postInvalidate()
gridToggle.setImageResource(
Expand Down Expand Up @@ -788,6 +860,34 @@ class SettingsDialog(val mActivity: MainActivity, themedContext: Context) :
slideDialogDown()
}

fun reloadSettings() {
if (camConfig.isVideoMode) {
reloadQualities()
}

reloadImageFormats()
}

fun reloadImageFormats() {
val imageFormats = getAvailableImageFormatTitles()

imageFormatAdapter = ArrayAdapter<String>(
mActivity,
android.R.layout.simple_spinner_item,
imageFormats
)

imageFormatAdapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item
)

imageFormatSpinner.adapter = imageFormatAdapter

imageFormatSpinner.setSelection(
camConfig.getAvailableImageFormats().indexOf(camConfig.outputImageFormat)
)
}

fun reloadQualities() {

val titles = getAvailableQTitles()
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/res/layout/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,39 @@

</LinearLayout>

<LinearLayout
android:id="@+id/image_format_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="@dimen/settings_dialog_menu_item_vertical"
android:paddingHorizontal="@dimen/settings_dialog_menu_item_horizontal"
android:layout_gravity="end"
android:orientation="horizontal">

<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/image_format"/>

<FrameLayout
android:layout_height="wrap_content"
android:padding="0dp"
android:layout_margin="0dp"
android:layout_width="match_parent">

<Spinner
android:id="@+id/image_format_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:padding="0dp"
android:layout_margin="0dp"
android:layout_gravity="end"/>

</FrameLayout>
</LinearLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/settings_dialog_menu_item_height"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
<string name="zsl_setting_title">Use ZSL in Latency mode</string>
<string name="zsl_setting_desc">Uses Zero Shutter Lag (ZSL) in Latency mode for faster capture. Certain devices may have a buggy implementation for this.</string>
<string name="audio_permission_failed_in_recording">Unable to request for audio permission in between a recording</string>
<string name="image_format">Image Format</string>

<string name="tap_to_mute_audio">Tap to mute audio</string>
<string name="tap_to_unmute_audio">Tap to unmute audio</string>
Expand Down