Skip to content
Open
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
41 changes: 40 additions & 1 deletion app/src/main/java/app/grapheneos/camera/CamConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class CamConfig(private val mActivity: MainActivity) {

const val WAIT_FOR_FOCUS_LOCK = "wait_for_focus_lock"

const val MAX_IMAGE_LONG_EDGE = "max_image_long_edge"

const val SELF_TIMER_DURATION = "self_timer_duration"
}

Expand Down Expand Up @@ -175,6 +177,9 @@ class CamConfig(private val mActivity: MainActivity) {

const val WAIT_FOR_FOCUS_LOCK = false

// 0 = no cap (use the largest resolution for the chosen aspect ratio)
const val MAX_IMAGE_LONG_EDGE = 0

const val SELF_TIMER_DURATION = 0
}
}
Expand Down Expand Up @@ -574,6 +579,21 @@ class CamConfig(private val mActivity: MainActivity) {
editor.apply()
}

// Cap for the long edge of captured photos, in pixels. 0 = no cap. Keeps documentation
// photos small independently of the aspect ratio / sensor.
var maxImageLongEdge: Int
get() {
return commonPref.getInt(
SettingValues.Key.MAX_IMAGE_LONG_EDGE,
SettingValues.Default.MAX_IMAGE_LONG_EDGE
)
}
set(value) {
val editor = commonPref.edit()
editor.putInt(SettingValues.Key.MAX_IMAGE_LONG_EDGE, value)
editor.apply()
}

var removeExifAfterCapture: Boolean
get() {
return commonPref.getBoolean(
Expand Down Expand Up @@ -1578,10 +1598,29 @@ class CamConfig(private val mActivity: MainActivity) {
val resolutionSelectorBuilder = ResolutionSelector.Builder()
.setAspectRatioStrategy(aspectRatioStrategy)

if (selectHighestResolution) {
// The cap and "highest resolution" are mutually exclusive in the UI; if both
// are somehow set, the cap below takes precedence.
if (selectHighestResolution && maxImageLongEdge == 0) {
resolutionSelectorBuilder.setAllowedResolutionMode(ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
}

// Cap the capture resolution when configured (0 = no cap). Picks the largest
// supported size whose long edge is <= the cap for the current aspect ratio.
val longEdgeCap = maxImageLongEdge
if (longEdgeCap > 0) {
val target = if (aspectRatio == AspectRatio.RATIO_16_9) {
Size(longEdgeCap, longEdgeCap * 9 / 16)
} else {
Size(longEdgeCap, longEdgeCap * 3 / 4)
}
resolutionSelectorBuilder.setResolutionStrategy(
ResolutionStrategy(
target,
ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER
)
)
}

it.setResolutionSelector(resolutionSelectorBuilder.build())

it.setFlashMode(flashMode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
private lateinit var rootView: View

private lateinit var pQField: EditText
private lateinit var mleField: EditText

private val dirPickerHandler = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
Expand Down Expand Up @@ -149,6 +150,10 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
pQField.filters = arrayOf(NumInputFilter(this))
pQField.setOnEditorActionListener(this)

mleField = binding.maxLongEdge
mleField.setText(camConfig.maxImageLongEdge.toString())
mleField.setOnEditorActionListener(this)

val exifToggle = binding.removeExifToggle
val exifToggleSetting = binding.removeExifSetting

Expand Down Expand Up @@ -241,11 +246,21 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
highResToggle.isChecked = camConfig.selectHighestResolution

highResToggle.setOnClickListener {
camConfig.selectHighestResolution = !camConfig.selectHighestResolution
camConfig.selectHighestResolution = highResToggle.isChecked
// "Highest resolution" and the resolution cap contradict each other; keep them
// mutually exclusive by clearing the cap when the highest-res mode is enabled.
if (highResToggle.isChecked && camConfig.maxImageLongEdge > 0) {
camConfig.maxImageLongEdge = 0
mleField.setText("0")
}
syncResolutionControls()
}

highResSetting.setOnClickListener {
highResToggle.performClick()
// Ignore taps while a resolution cap keeps this row disabled.
if (camConfig.maxImageLongEdge == 0) {
highResToggle.performClick()
}
}

if (!showStorageSettings) {
Expand All @@ -268,6 +283,8 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
v.setPadding(cutouts.left, 0, cutouts.right, 0)
insets
}

syncResolutionControls()
}

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
Expand Down Expand Up @@ -327,6 +344,37 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
} else {
camConfig.photoQuality = quality
}

// Dump state of max image resolution (long edge). 0 = no cap; empty/invalid -> revert.
val longEdge = mleField.text.toString().toIntOrNull()
if (longEdge == null || longEdge < 0) {
mleField.setText(camConfig.maxImageLongEdge.toString())
if (notifyOnInvalidValue) {
showMessage(getString(R.string.invalid_max_long_edge_value))
}
} else {
camConfig.maxImageLongEdge = longEdge
// Mutually exclusive with "highest resolution": a cap takes precedence.
if (longEdge > 0 && camConfig.selectHighestResolution) {
camConfig.selectHighestResolution = false
binding.highestResSettingToggle.isChecked = false
}
}

syncResolutionControls()
}

// "Highest resolution" and the resolution cap are mutually exclusive; reflect that by
// disabling whichever control the other one currently overrides.
private fun syncResolutionControls() {
val capActive = camConfig.maxImageLongEdge > 0
val highestActive = camConfig.selectHighestResolution

mleField.isEnabled = !highestActive
binding.maxLongEdgeSetting.alpha = if (highestActive) 0.5f else 1f

binding.highestResSettingToggle.isEnabled = !capActive
binding.highestResSetting.alpha = if (capActive) 0.5f else 1f
}

override fun onEditorAction(p0: TextView?, id: Int, p2: KeyEvent?): Boolean {
Expand Down
85 changes: 85 additions & 0 deletions app/src/main/res/layout/more_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,91 @@
</LinearLayout>


<LinearLayout
android:id="@+id/max_long_edge_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingHorizontal="16dp"
android:paddingBottom="10dp">

<ImageView
android:id="@+id/max_long_edge_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:importantForAccessibility="no"
android:paddingStart="4dp"
android:paddingEnd="8dp"
android:src="@drawable/megapixel" />

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:orientation="vertical">

<TextView
android:id="@+id/max_long_edge_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:paddingBottom="2dp"
android:text="@string/max_long_edge_title"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />

<TextView
android:id="@+id/max_long_edge_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:text="@string/max_long_edge_desc"
android:textSize="14sp"
tools:ignore="RtlSymmetry" />

</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">

<EditText
android:id="@+id/max_long_edge"
android:layout_width="52dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="2dp"
android:clickable="true"
android:focusableInTouchMode="true"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="number"
android:labelFor="@id/max_long_edge_setting"
android:maxLength="4"
android:paddingTop="15dp"
android:textAlignment="center"
android:textSize="16sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="4dp"
android:paddingTop="16dp"
android:text="@string/pixel_symbol"
android:textSize="16sp" />

</LinearLayout>

</LinearLayout>


<LinearLayout
android:id="@+id/highest_res_setting"
android:layout_width="match_parent"
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,10 @@

<string name="wait_for_focus_lock">Wait for Focus Lock</string>
<string name="invalid_photo_quality_value">Invalid value set for photo quality</string>

<!-- Max resolution (long edge) setting -->
<string name="max_long_edge_title">Max resolution</string>
<string name="max_long_edge_desc">Longest edge in pixels for captured photos. 0 = no limit. If the value is below the smallest size the camera supports, that smallest size is used.</string>
<string name="pixel_symbol">px</string>
<string name="invalid_max_long_edge_value">Invalid value set for maximum resolution</string>
</resources>