@@ -2,28 +2,30 @@ package app.grapheneos.camera.ui.activities
22
33import android.content.Context
44import android.content.Intent
5- import android.graphics.Color
65import android.graphics.Rect
76import android.os.Bundle
87import android.view.KeyEvent
98import android.view.MotionEvent
109import android.view.View
1110import android.view.inputmethod.EditorInfo
1211import android.view.inputmethod.InputMethodManager
12+ import android.widget.AdapterView
13+ import android.widget.ArrayAdapter
14+ import android.widget.AutoCompleteTextView
1315import android.widget.Button
1416import android.widget.EditText
1517import android.widget.TextView
1618import androidx.activity.enableEdgeToEdge
1719import androidx.activity.result.contract.ActivityResultContracts
1820import androidx.appcompat.app.AppCompatActivity
19- import androidx.core.view.OnApplyWindowInsetsListener
2021import androidx.core.view.ViewCompat
2122import androidx.core.view.WindowInsetsCompat
2223import app.grapheneos.camera.CamConfig
2324import app.grapheneos.camera.CapturedItems
24- import app.grapheneos.camera.NumInputFilter
25+ import app.grapheneos.camera.inputfilter.NumLimitFilter
2526import app.grapheneos.camera.R
2627import app.grapheneos.camera.databinding.MoreSettingsBinding
28+ import app.grapheneos.camera.inputfilter.CustomNumFilter
2729import app.grapheneos.camera.util.storageLocationToUiString
2830import com.google.android.material.dialog.MaterialAlertDialogBuilder
2931import com.google.android.material.snackbar.Snackbar
@@ -44,6 +46,10 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
4446 private lateinit var iFField: EditText
4547 private lateinit var vFField: EditText
4648
49+ private lateinit var videoBitRateValueField: EditText
50+ private lateinit var videoBitRateUnitField: AutoCompleteTextView
51+ private lateinit var videoBitRateUnitFieldAdapter: ArrayAdapter <String >
52+
4753 private val dirPickerHandler = registerForActivityResult(
4854 ActivityResultContracts .StartActivityForResult ()
4955 ) {
@@ -150,9 +156,130 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
150156 pQField.setText(camConfig.photoQuality.toString())
151157 }
152158
153- pQField.filters = arrayOf(NumInputFilter (this ))
159+ pQField.filters = arrayOf(NumLimitFilter (
160+ min = PHOTO_QUALITY_MIN ,
161+ max = PHOTO_QUALITY_MAX ,
162+ onOutOfRange = {
163+ showMessage(getString(R .string.photo_quality_number_limit, PHOTO_QUALITY_MIN , PHOTO_QUALITY_MAX ))
164+ }
165+ ))
166+ pQField.onFocusChangeListener = object : View .OnFocusChangeListener {
167+ override fun onFocusChange (v : View , hasFocus : Boolean ) {
168+ if (! hasFocus) {
169+ if (pQField.text.isEmpty()) {
170+ camConfig.photoQuality = 0
171+
172+ showMessage(
173+ getString(R .string.photo_quality_was_set_to_auto)
174+ )
175+ } else {
176+ try {
177+ camConfig.photoQuality =
178+ Integer .parseInt(pQField.text.toString())
179+ } catch (_: Exception ) {
180+ camConfig.photoQuality = 0
181+
182+ }
183+ }
184+ }
185+ }
186+ }
154187 pQField.setOnEditorActionListener(this )
155188
189+ // Video bitrate value field
190+ videoBitRateValueField = binding.videoBitrateValueField
191+
192+ if (camConfig.videoBitRateValue != 0 ) {
193+ videoBitRateValueField.setText(camConfig.videoBitRateValue.toString())
194+ }
195+
196+ videoBitRateValueField.filters = arrayOf(
197+ CustomNumFilter (
198+ shouldAcceptNumber = { value ->
199+ val calculatedBitRate = value * camConfig.videoBitRateMultiplier
200+ if (calculatedBitRate >= VIDEO_BITRATE_MIN && calculatedBitRate <= VIDEO_BITRATE_MAX ) {
201+ true
202+ } else {
203+ val minVideoBitrateInUnits = (VIDEO_BITRATE_MIN .toFloat() / camConfig.videoBitRateMultiplier).coerceAtLeast(1f ).toInt()
204+ val maxVideoBitrateInUnits = VIDEO_BITRATE_MAX / camConfig.videoBitRateMultiplier
205+
206+ showMessage(getString(R .string.video_bitrate_number_limit, minVideoBitrateInUnits, camConfig.videoBitRateUnit, maxVideoBitrateInUnits, camConfig.videoBitRateUnit))
207+ false
208+ }
209+ }
210+ )
211+ )
212+ videoBitRateValueField.onFocusChangeListener = object : View .OnFocusChangeListener {
213+ override fun onFocusChange (v : View , hasFocus : Boolean ) {
214+ if (! hasFocus) {
215+ if (videoBitRateValueField.text.isEmpty()) {
216+ camConfig.videoBitRateValue = 0
217+
218+ showMessage(
219+ getString(R .string.video_bitrate_was_set_to_auto)
220+ )
221+ } else {
222+ try {
223+ camConfig.videoBitRateValue =
224+ Integer .parseInt(videoBitRateValueField.text.toString())
225+ } catch (exception: Exception ) {
226+ camConfig.videoBitRateValue = 0
227+ showMessage(
228+ getString(R .string.video_bitrate_was_set_to_auto)
229+ )
230+ }
231+ }
232+ }
233+ }
234+ }
235+ videoBitRateValueField.setOnEditorActionListener(this )
236+
237+ // Video bit rate unit field
238+ videoBitRateUnitField = binding.videoBitrateUnitSpinner
239+
240+ videoBitRateUnitFieldAdapter = ArrayAdapter <String >(
241+ this ,
242+ android.R .layout.simple_spinner_item,
243+ CamConfig .VIDEO_BITRATE_UNITS
244+ )
245+
246+ videoBitRateUnitFieldAdapter.setDropDownViewResource(
247+ android.R .layout.simple_spinner_dropdown_item
248+ )
249+
250+ videoBitRateUnitField.setAdapter(videoBitRateUnitFieldAdapter)
251+
252+ videoBitRateUnitField.setText(camConfig.videoBitRateUnit, false )
253+
254+ videoBitRateUnitField.onItemClickListener = object : AdapterView .OnItemClickListener {
255+ override fun onItemClick (
256+ parent : AdapterView <* >? ,
257+ view : View ? ,
258+ position : Int ,
259+ id : Long
260+ ) {
261+ val selectedBitRateUnit = CamConfig .VIDEO_BITRATE_UNITS [position]
262+ camConfig.videoBitRateUnit = selectedBitRateUnit
263+
264+ // Update bitrate value to highest possible valid value when it exceeds VIDEO_BITRATE_MAX
265+ // while switching to a higher value unit (as a fallback to prevent overflow while switching
266+ // from say MAX_INT b/s to MAX_INT mb/s (b/s --> mb/s) which would lead to unexpected behavior
267+ val maxVideoBitrateWithUnits = VIDEO_BITRATE_MAX / camConfig.videoBitRateMultiplier
268+ if (camConfig.videoBitRateValue > maxVideoBitrateWithUnits) {
269+ camConfig.videoBitRateValue = maxVideoBitrateWithUnits
270+ videoBitRateValueField.setText(maxVideoBitrateWithUnits.toString())
271+ }
272+ }
273+ }
274+
275+ binding.refreshVideoBitrate.setOnClickListener {
276+ camConfig.videoBitRateValue = 0
277+ videoBitRateValueField.setText(" " )
278+ showMessage(
279+ getString(R .string.video_bitrate_was_set_to_auto)
280+ )
281+ }
282+
156283 iFField = binding.imageFormatSettingField
157284 iFField.setOnEditorActionListener(this )
158285
@@ -267,7 +394,7 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
267394 v.getGlobalVisibleRect(outRect)
268395 if (! outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
269396 clearFocus()
270- dumpData()
397+ // dumpData()
271398 }
272399 }
273400 }
@@ -284,39 +411,10 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
284411 }
285412 }
286413
287- private fun dumpData () {
288-
289- // Dump state of photo quality
290- if (pQField.text.isEmpty()) {
291- camConfig.photoQuality = 0
292-
293- showMessage(
294- getString(R .string.photo_quality_was_set_to_auto)
295- )
296- } else {
297- try {
298-
299- camConfig.photoQuality =
300- Integer .parseInt(pQField.text.toString())
301-
302- } catch (exception: Exception ) {
303-
304- camConfig.photoQuality = 0
305-
306- }
307- }
308-
309- // // Dump state of image format
310- // camConfig.imageFormat = iFField.text.toString()
311- //
312- // // Dump state of video format
313- // camConfig.videoFormat = vFField.text.toString()
314- }
315-
316414 override fun onEditorAction (p0 : TextView ? , id : Int , p2 : KeyEvent ? ): Boolean {
317415 return if (id == EditorInfo .IME_ACTION_DONE ) {
318416 clearFocus()
319- dumpData()
417+ // dumpData()
320418 true
321419 } else false
322420 }
@@ -332,6 +430,13 @@ open class MoreSettings : AppCompatActivity(), TextView.OnEditorActionListener {
332430 }
333431
334432 companion object {
433+ const val PHOTO_QUALITY_MIN = 1
434+ const val PHOTO_QUALITY_MAX = 100
435+
436+ // Video bitrate in bs
437+ const val VIDEO_BITRATE_MIN = 1
438+ const val VIDEO_BITRATE_MAX = Integer .MAX_VALUE
439+
335440 private var camConfigId = 0L
336441 private var staticCamConfig: CamConfig ? = null
337442
0 commit comments