@@ -3,6 +3,8 @@ package app.grapheneos.camera
33import android.annotation.SuppressLint
44import android.content.Context
55import android.content.SharedPreferences
6+ import android.graphics.ImageFormat
7+ import android.hardware.camera2.CameraCharacteristics
68import android.net.Uri
79import android.os.Build
810import android.provider.MediaStore
@@ -32,6 +34,7 @@ import androidx.camera.core.featuregroup.GroupableFeature
3234import androidx.camera.core.resolutionselector.AspectRatioStrategy
3335import androidx.camera.core.resolutionselector.ResolutionSelector
3436import androidx.camera.core.resolutionselector.ResolutionStrategy
37+ import androidx.camera.camera2.interop.Camera2CameraInfo
3538import androidx.camera.extensions.ExtensionMode
3639import androidx.camera.extensions.ExtensionsManager
3740import androidx.camera.lifecycle.ProcessCameraProvider
@@ -119,6 +122,8 @@ class CamConfig(private val mActivity: MainActivity) {
119122
120123 const val WAIT_FOR_FOCUS_LOCK = " wait_for_focus_lock"
121124
125+ const val CAPTURE_RESOLUTION = " capture_resolution"
126+
122127 // const val IMAGE_FILE_FORMAT = "image_quality"
123128 // const val VIDEO_FILE_FORMAT = "video_quality"
124129 }
@@ -179,6 +184,14 @@ class CamConfig(private val mActivity: MainActivity) {
179184
180185 const val DEFAULT_LENS_FACING = CameraSelector .LENS_FACING_BACK
181186
187+ fun aspectRatioToWidthHeight (aspectRatio : Int ): Pair <Int , Int > {
188+ return when (aspectRatio) {
189+ AspectRatio .RATIO_16_9 -> Pair (16 , 9 )
190+ AspectRatio .RATIO_4_3 -> Pair (4 , 3 )
191+ else -> throw IllegalArgumentException (" Unknown aspect ratio: $aspectRatio " )
192+ }
193+ }
194+
182195 val commonFormats = arrayOf(
183196 BarcodeFormat .AZTEC ,
184197 BarcodeFormat .QR_CODE ,
@@ -722,6 +735,8 @@ class CamConfig(private val mActivity: MainActivity) {
722735
723736 if (isVideoMode) {
724737 mActivity.settingsDialog.reloadQualities()
738+ } else {
739+ mActivity.settingsDialog.reloadResolutions()
725740 }
726741
727742 if (lensFacing == CameraSelector .LENS_FACING_FRONT ) {
@@ -923,6 +938,27 @@ class CamConfig(private val mActivity: MainActivity) {
923938 }
924939 }
925940
941+ var captureResolution: Size ?
942+ get() {
943+ val value = commonPref.getString(SettingValues .Key .CAPTURE_RESOLUTION , null )
944+ if (value.isNullOrEmpty()) return null
945+ return try {
946+ val parts = value.split(" x" )
947+ Size (parts[0 ].toInt(), parts[1 ].toInt())
948+ } catch (e: Exception ) {
949+ null
950+ }
951+ }
952+ set(value) {
953+ commonPref.edit {
954+ if (value == null ) {
955+ remove(SettingValues .Key .CAPTURE_RESOLUTION )
956+ } else {
957+ putString(SettingValues .Key .CAPTURE_RESOLUTION , " ${value.width} x${value.height} " )
958+ }
959+ }
960+ }
961+
926962 var selectHighestResolution: Boolean
927963 get() {
928964 return commonPref.getBoolean(
@@ -989,13 +1025,38 @@ class CamConfig(private val mActivity: MainActivity) {
9891025 } else {
9901026 AspectRatio .RATIO_16_9
9911027 }
1028+ // Clear capture resolution since available resolutions depend on aspect ratio
1029+ captureResolution = null
9921030 startCamera(true )
9931031 }
9941032
9951033 private fun getCurrentCameraInfo () : CameraInfo {
9961034 return cameraProvider!! .getCameraInfo(cameraSelector)
9971035 }
9981036
1037+ @SuppressLint(" UnsafeOptInUsageError" )
1038+ fun getAvailableImageResolutions (): List <Size > {
1039+ val cameraInfo = camera?.cameraInfo ? : return emptyList()
1040+ val camera2Info = Camera2CameraInfo .from(cameraInfo)
1041+ val characteristics = camera2Info.getCameraCharacteristic(
1042+ CameraCharacteristics .SCALER_STREAM_CONFIGURATION_MAP
1043+ ) ? : return emptyList()
1044+
1045+ val sizes = characteristics.getOutputSizes(ImageFormat .JPEG ) ? : return emptyList()
1046+
1047+ // Filter by current aspect ratio with 2% tolerance
1048+ val targetRatio = when (aspectRatio) {
1049+ AspectRatio .RATIO_16_9 -> 16.0 / 9.0
1050+ AspectRatio .RATIO_4_3 -> 4.0 / 3.0
1051+ else -> 4.0 / 3.0
1052+ }
1053+
1054+ return sizes.filter { size ->
1055+ val ratio = size.width.toDouble() / size.height.toDouble()
1056+ kotlin.math.abs(ratio - targetRatio) / targetRatio < 0.02
1057+ }.sortedByDescending { it.width * it.height }
1058+ }
1059+
9991060 fun toggleCameraSelector () {
10001061
10011062 // Manually switch to the opposite lens facing
@@ -1229,6 +1290,12 @@ class CamConfig(private val mActivity: MainActivity) {
12291290 resolutionSelectorBuilder.setAllowedResolutionMode(ResolutionSelector .PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE )
12301291 }
12311292
1293+ captureResolution?.let { size ->
1294+ resolutionSelectorBuilder.setResolutionStrategy(
1295+ ResolutionStrategy (size, ResolutionStrategy .FALLBACK_RULE_NONE )
1296+ )
1297+ }
1298+
12321299 it.setResolutionSelector(resolutionSelectorBuilder.build())
12331300
12341301 it.setFlashMode(flashMode)
0 commit comments