Merge pull request #330 from KryptKode/feat/camera-x

Updates/Fixes for CameraX implementation
This commit is contained in:
Tibor Kaputa 2022-07-13 08:54:12 +02:00 committed by GitHub
commit 91bc6ccf60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 363 additions and 319 deletions

View File

@ -63,13 +63,13 @@ android {
} }
dependencies { dependencies {
implementation 'com.github.SimpleMobileTools:Simple-Commons:d1d5402388' implementation 'com.github.SimpleMobileTools:Simple-Commons:7c48da6bef'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation "androidx.exifinterface:exifinterface:1.3.3" implementation "androidx.exifinterface:exifinterface:1.3.3"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.0"
implementation 'androidx.window:window:1.1.0-alpha02' implementation 'androidx.window:window:1.1.0-alpha02'
def camerax_version = '1.1.0-rc02' def camerax_version = '1.1.0'
implementation "androidx.camera:camera-core:$camerax_version" implementation "androidx.camera:camera-core:$camerax_version"
implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-video:$camerax_version" implementation "androidx.camera:camera-video:$camerax_version"

View File

@ -10,10 +10,11 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.LICENSE_GLIDE import com.simplemobiletools.commons.helpers.LICENSE_GLIDE
import com.simplemobiletools.commons.helpers.NavigationIcon
import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import java.util.Locale
import kotlinx.android.synthetic.main.activity_settings.* import kotlinx.android.synthetic.main.activity_settings.*
import java.util.*
class SettingsActivity : SimpleActivity() { class SettingsActivity : SimpleActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -23,7 +24,7 @@ class SettingsActivity : SimpleActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setupToolbar(settings_toolbar, NavigationIcon.Arrow)
setupPurchaseThankYou() setupPurchaseThankYou()
setupCustomizeColors() setupCustomizeColors()
setupUseEnglish() setupUseEnglish()

View File

@ -8,13 +8,19 @@ import com.simplemobiletools.camera.activities.SimpleActivity
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.dialog_change_resolution.view.* import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo_holder
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_video
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_video_holder
class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Boolean, val photoResolutions: ArrayList<MySize>, class ChangeResolutionDialog(
val videoResolutions: ArrayList<MySize>, val openVideoResolutions: Boolean, val callback: () -> Unit) { val activity: SimpleActivity, val isFrontCamera: Boolean, val photoResolutions: ArrayList<MySize>,
private var dialog: AlertDialog val videoResolutions: ArrayList<MySize>, val openVideoResolutions: Boolean, val callback: () -> Unit
) {
private var dialog: AlertDialog? = null
private val config = activity.config private val config = activity.config
init { init {
@ -23,16 +29,18 @@ class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Bo
setupVideoResolutionPicker(this) setupVideoResolutionPicker(this)
} }
dialog = AlertDialog.Builder(activity) activity.getAlertDialogBuilder()
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setOnDismissListener { callback() } .setOnDismissListener { callback() }
.create().apply { .apply {
activity.setupDialogStuff(view, this, if (isFrontCamera) R.string.front_camera else R.string.back_camera) { val titleId = if (isFrontCamera) R.string.front_camera else R.string.back_camera
if (openVideoResolutions) { activity.setupDialogStuff(view, this, titleId) { alertDialog ->
view.change_resolution_video_holder.performClick() dialog = alertDialog
} if (openVideoResolutions) {
view.change_resolution_video_holder.performClick()
} }
} }
}
} }
private fun setupPhotoResolutionPicker(view: View) { private fun setupPhotoResolutionPicker(view: View) {
@ -49,7 +57,7 @@ class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Bo
} else { } else {
config.backPhotoResIndex = it config.backPhotoResIndex = it
} }
dialog.dismiss() dialog?.dismiss()
} }
} }
view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title
@ -68,7 +76,7 @@ class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Bo
} else { } else {
config.backVideoResIndex = it config.backVideoResIndex = it
} }
dialog.dismiss() dialog?.dismiss()
} }
} }
view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title

View File

@ -10,6 +10,7 @@ import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.camera.models.VideoQuality import com.simplemobiletools.camera.models.VideoQuality
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo
@ -22,9 +23,9 @@ class ChangeResolutionDialogX(
private val isFrontCamera: Boolean, private val isFrontCamera: Boolean,
private val photoResolutions: List<MySize> = listOf(), private val photoResolutions: List<MySize> = listOf(),
private val videoResolutions: List<VideoQuality>, private val videoResolutions: List<VideoQuality>,
private val callback: () -> Unit private val callback: () -> Unit,
) { ) {
private var dialog: AlertDialog private var dialog: AlertDialog? = null
private val config = activity.config private val config = activity.config
private val TAG = "ChangeResolutionDialogX" private val TAG = "ChangeResolutionDialogX"
@ -34,20 +35,27 @@ class ChangeResolutionDialogX(
setupVideoResolutionPicker(this) setupVideoResolutionPicker(this)
} }
dialog = AlertDialog.Builder(activity) activity.getAlertDialogBuilder()
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.create().apply { .apply {
activity.setupDialogStuff(view, this, if (isFrontCamera) R.string.front_camera else R.string.back_camera) val titleId = if (isFrontCamera) R.string.front_camera else R.string.back_camera
activity.setupDialogStuff(view, this, titleId) { alertDialog ->
dialog = alertDialog
}
} }
} }
private fun setupPhotoResolutionPicker(view: View) { private fun setupPhotoResolutionPicker(view: View) {
val items = getFormattedResolutions(photoResolutions) val items = photoResolutions.mapIndexed { index, resolution ->
val megapixels = resolution.megaPixels
val aspectRatio = resolution.getAspectRatio(activity)
RadioItem(index, "${resolution.width} x ${resolution.height} ($megapixels MP, $aspectRatio)")
}
var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex
selectionIndex = Math.max(selectionIndex, 0) selectionIndex = selectionIndex.coerceAtLeast(0)
view.change_resolution_photo_holder.setOnClickListener { view.change_resolution_photo_holder.setOnClickListener {
RadioGroupDialog(activity, items, selectionIndex) { RadioGroupDialog(activity, ArrayList(items), selectionIndex) {
selectionIndex = it as Int selectionIndex = it as Int
Log.w(TAG, "setupPhotoResolutionPicker: selectionIndex=$it") Log.w(TAG, "setupPhotoResolutionPicker: selectionIndex=$it")
view.change_resolution_photo.text = items[selectionIndex].title view.change_resolution_photo.text = items[selectionIndex].title
@ -56,7 +64,7 @@ class ChangeResolutionDialogX(
} else { } else {
config.backPhotoResIndex = it config.backPhotoResIndex = it
} }
dialog.dismiss() dialog?.dismiss()
callback.invoke() callback.invoke()
} }
} }
@ -70,35 +78,27 @@ class ChangeResolutionDialogX(
RadioItem(index, "${videoQuality.width} x ${videoQuality.height} ($megapixels MP, $aspectRatio)") RadioItem(index, "${videoQuality.width} x ${videoQuality.height} ($megapixels MP, $aspectRatio)")
} }
val videoQuality = if (isFrontCamera) config.frontVideoQuality else config.backVideoQuality var selectionIndex = if (isFrontCamera) config.frontVideoResIndex else config.backVideoResIndex
var selectionIndex = videoResolutions.indexOf(videoQuality) selectionIndex = selectionIndex.coerceAtLeast(0)
Log.i(TAG, "videoResolutions=$videoResolutions")
Log.i(TAG, "setupVideoResolutionPicker: selectionIndex=$selectionIndex")
view.change_resolution_video_holder.setOnClickListener { view.change_resolution_video_holder.setOnClickListener {
RadioGroupDialog(activity, ArrayList(items), selectionIndex) { RadioGroupDialog(activity, ArrayList(items), selectionIndex) {
selectionIndex = it as Int selectionIndex = it as Int
val selectedItem = items[selectionIndex] val selectedItem = items[selectionIndex]
val selectedQuality = videoResolutions[selectionIndex]
view.change_resolution_video.text = selectedItem.title view.change_resolution_video.text = selectedItem.title
if (isFrontCamera) { if (isFrontCamera) {
config.frontVideoQuality = selectedQuality config.frontVideoResIndex = selectionIndex
} else { } else {
config.backVideoQuality = selectedQuality config.backPhotoResIndex = selectionIndex
} }
dialog.dismiss() dialog?.dismiss()
callback.invoke() callback.invoke()
} }
} }
view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title val selectedItem = items.getOrNull(selectionIndex)
} view.change_resolution_video.text = selectedItem?.title
Log.i(TAG, "setupVideoResolutionPicker: selectedItem=$selectedItem")
private fun getFormattedResolutions(resolutions: List<MySize>): ArrayList<RadioItem> {
val items = ArrayList<RadioItem>(resolutions.size)
val sorted = resolutions.sortedByDescending { it.width * it.height }
sorted.forEachIndexed { index, size ->
val megapixels = String.format("%.1f", (size.width * size.height.toFloat()) / 1000000)
val aspectRatio = size.getAspectRatio(activity)
items.add(RadioItem(index, "${size.width} x ${size.height} ($megapixels MP, $aspectRatio)"))
}
return items
} }
} }

View File

@ -3,7 +3,6 @@ package com.simplemobiletools.camera.helpers
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import com.simplemobiletools.camera.models.VideoQuality
import com.simplemobiletools.commons.helpers.BaseConfig import com.simplemobiletools.commons.helpers.BaseConfig
import java.io.File import java.io.File
@ -63,20 +62,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(FRONT_PHOTO_RESOLUTION_INDEX, 0) get() = prefs.getInt(FRONT_PHOTO_RESOLUTION_INDEX, 0)
set(frontPhotoResIndex) = prefs.edit().putInt(FRONT_PHOTO_RESOLUTION_INDEX, frontPhotoResIndex).apply() set(frontPhotoResIndex) = prefs.edit().putInt(FRONT_PHOTO_RESOLUTION_INDEX, frontPhotoResIndex).apply()
var backVideoQuality: VideoQuality
get() {
val backQuality = prefs.getString(BACK_VIDEO_QUALITY, VideoQuality.UHD.name)
return VideoQuality.values().first { it.name == backQuality }
}
set(backVideoQuality) = prefs.edit().putString(BACK_VIDEO_QUALITY, backVideoQuality.name).apply()
var frontVideoQuality: VideoQuality
get() {
val frontQuality = prefs.getString(FRONT_VIDEO_QUALITY, VideoQuality.UHD.name)
return VideoQuality.values().first { it.name == frontQuality }
}
set(frontVideoQuality) = prefs.edit().putString(FRONT_VIDEO_QUALITY, frontVideoQuality.name).apply()
var frontVideoResIndex: Int var frontVideoResIndex: Int
get() = prefs.getInt(FRONT_VIDEO_RESOLUTION_INDEX, 0) get() = prefs.getInt(FRONT_VIDEO_RESOLUTION_INDEX, 0)
set(frontVideoResIndex) = prefs.edit().putInt(FRONT_VIDEO_RESOLUTION_INDEX, frontVideoResIndex).apply() set(frontVideoResIndex) = prefs.edit().putInt(FRONT_VIDEO_RESOLUTION_INDEX, frontVideoResIndex).apply()

View File

@ -10,11 +10,8 @@ import android.util.Log
import android.util.Size import android.util.Size
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.video.Quality
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.toCameraXQuality
import com.simplemobiletools.camera.models.CameraSelectorImageQualities import com.simplemobiletools.camera.models.CameraSelectorImageQualities
import com.simplemobiletools.camera.models.CameraSelectorVideoQualities
import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.camera.models.MySize
class ImageQualityManager( class ImageQualityManager(
@ -69,7 +66,7 @@ class ImageQualityManager(
val index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex val index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex
return imageQualities.filter { it.camSelector == cameraSelector } return imageQualities.filter { it.camSelector == cameraSelector }
.flatMap { it.qualities } .flatMap { it.qualities }
.sortedByDescending { it.pixels} .sortedByDescending { it.pixels }
.distinctBy { it.pixels } .distinctBy { it.pixels }
.map { Size(it.width, it.height) } .map { Size(it.width, it.height) }
.also { .also {
@ -85,5 +82,6 @@ class ImageQualityManager(
.flatMap { it.qualities } .flatMap { it.qualities }
.sortedByDescending { it.pixels } .sortedByDescending { it.pixels }
.distinctBy { it.pixels } .distinctBy { it.pixels }
.filter { it.megaPixels != "0.0" }
} }
} }

View File

@ -1,17 +1,20 @@
package com.simplemobiletools.camera.helpers package com.simplemobiletools.camera.helpers
import android.util.Log import android.util.Log
import androidx.camera.core.Camera import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.Quality import androidx.camera.video.Quality
import androidx.camera.video.QualitySelector import androidx.camera.video.QualitySelector
import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.toCameraXQuality import com.simplemobiletools.camera.extensions.toCameraXQuality
import com.simplemobiletools.camera.extensions.toVideoQuality import com.simplemobiletools.camera.extensions.toVideoQuality
import com.simplemobiletools.camera.models.CameraSelectorVideoQualities import com.simplemobiletools.camera.models.CameraSelectorVideoQualities
import com.simplemobiletools.camera.models.VideoQuality import com.simplemobiletools.camera.models.VideoQuality
class VideoQualityManager(private val config: Config) { class VideoQualityManager(
private val activity: AppCompatActivity,
) {
companion object { companion object {
private const val TAG = "VideoQualityHelper" private const val TAG = "VideoQualityHelper"
@ -19,14 +22,14 @@ class VideoQualityManager(private val config: Config) {
private val CAMERA_SELECTORS = arrayOf(CameraSelector.DEFAULT_BACK_CAMERA, CameraSelector.DEFAULT_FRONT_CAMERA) private val CAMERA_SELECTORS = arrayOf(CameraSelector.DEFAULT_BACK_CAMERA, CameraSelector.DEFAULT_FRONT_CAMERA)
} }
private val config = activity.config
private val videoQualities = mutableListOf<CameraSelectorVideoQualities>() private val videoQualities = mutableListOf<CameraSelectorVideoQualities>()
fun initSupportedQualities( fun initSupportedQualities(cameraProvider: ProcessCameraProvider) {
cameraProvider: ProcessCameraProvider,
camera: Camera,
) {
if (videoQualities.isEmpty()) { if (videoQualities.isEmpty()) {
for (camSelector in CAMERA_SELECTORS) { for (camSelector in CAMERA_SELECTORS) {
cameraProvider.unbindAll()
val camera = cameraProvider.bindToLifecycle(activity, camSelector)
try { try {
if (cameraProvider.hasCamera(camSelector)) { if (cameraProvider.hasCamera(camSelector)) {
QualitySelector.getSupportedQualities(camera.cameraInfo) QualitySelector.getSupportedQualities(camera.cameraInfo)
@ -34,6 +37,7 @@ class VideoQualityManager(private val config: Config) {
.also { allQualities -> .also { allQualities ->
val qualities = allQualities.map { it.toVideoQuality() } val qualities = allQualities.map { it.toVideoQuality() }
videoQualities.add(CameraSelectorVideoQualities(camSelector, qualities)) videoQualities.add(CameraSelectorVideoQualities(camSelector, qualities))
} }
Log.i(TAG, "bindCameraUseCases: videoQualities=$videoQualities") Log.i(TAG, "bindCameraUseCases: videoQualities=$videoQualities")
} }
@ -45,11 +49,9 @@ class VideoQualityManager(private val config: Config) {
} }
fun getUserSelectedQuality(cameraSelector: CameraSelector): Quality { fun getUserSelectedQuality(cameraSelector: CameraSelector): Quality {
return if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) { var selectionIndex = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontVideoResIndex else config.backVideoResIndex
config.frontVideoQuality.toCameraXQuality() selectionIndex = selectionIndex.coerceAtLeast(0)
} else { return getSupportedQualities(cameraSelector)[selectionIndex].toCameraXQuality()
config.backVideoQuality.toCameraXQuality()
}
} }
fun getSupportedQualities(cameraSelector: CameraSelector): List<VideoQuality> { fun getSupportedQualities(cameraSelector: CameraSelector): List<VideoQuality> {

View File

@ -5,6 +5,7 @@ import android.content.Context
import android.hardware.SensorManager import android.hardware.SensorManager
import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager
import android.util.Log import android.util.Log
import android.util.Size
import android.view.* import android.view.*
import android.view.GestureDetector.SimpleOnGestureListener import android.view.GestureDetector.SimpleOnGestureListener
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -57,7 +58,7 @@ class CameraXPreview(
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
private val mediaSoundHelper = MediaSoundHelper() private val mediaSoundHelper = MediaSoundHelper()
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate() private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
private val videoQualityManager = VideoQualityManager(config) private val videoQualityManager = VideoQualityManager(activity)
private val imageQualityManager = ImageQualityManager(activity) private val imageQualityManager = ImageQualityManager(activity)
private val exifRemover = ExifRemover(contentResolver) private val exifRemover = ExifRemover(contentResolver)
@ -74,7 +75,7 @@ class CameraXPreview(
in 225 until 315 -> Surface.ROTATION_90 in 225 until 315 -> Surface.ROTATION_90
else -> Surface.ROTATION_0 else -> Surface.ROTATION_0
} }
Log.i(TAG, "onOrientationChanged: rotation=$rotation")
preview?.targetRotation = rotation preview?.targetRotation = rotation
imageCapture?.targetRotation = rotation imageCapture?.targetRotation = rotation
videoCapture?.targetRotation = rotation videoCapture?.targetRotation = rotation
@ -109,10 +110,13 @@ class CameraXPreview(
private fun startCamera(switching: Boolean = false) { private fun startCamera(switching: Boolean = false) {
Log.i(TAG, "startCamera: ") Log.i(TAG, "startCamera: ")
imageQualityManager.initSupportedQualities() imageQualityManager.initSupportedQualities()
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity) val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
cameraProviderFuture.addListener({ cameraProviderFuture.addListener({
try { try {
cameraProvider = cameraProviderFuture.get() val provider = cameraProviderFuture.get()
cameraProvider = provider
videoQualityManager.initSupportedQualities(provider)
bindCameraUseCases() bindCameraUseCases()
setupCameraObservers() setupCameraObservers()
} catch (e: Exception) { } catch (e: Exception) {
@ -142,9 +146,7 @@ class CameraXPreview(
cameraSelector, cameraSelector,
preview, preview,
captureUseCase, captureUseCase,
).also { )
videoQualityManager.initSupportedQualities(cameraProvider, it)
}
preview?.setSurfaceProvider(previewView.surfaceProvider) preview?.setSurfaceProvider(previewView.surfaceProvider)
setupZoomAndFocus() setupZoomAndFocus()
@ -187,20 +189,31 @@ class CameraXPreview(
} }
private fun buildImageCapture(aspectRatio: Int, rotation: Int): ImageCapture { private fun buildImageCapture(aspectRatio: Int, rotation: Int): ImageCapture {
return ImageCapture.Builder() return Builder()
.setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY) .setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
.setFlashMode(flashMode) .setFlashMode(flashMode)
.setJpegQuality(config.photoQuality) .setJpegQuality(config.photoQuality)
.setTargetRotation(rotation) .setTargetRotation(rotation)
.apply { .apply {
imageQualityManager.getUserSelectedResolution(cameraSelector)?.let { resolution -> imageQualityManager.getUserSelectedResolution(cameraSelector)?.let { resolution ->
val rotatedResolution = getRotatedResolution(rotation, resolution)
Log.i(TAG, "buildImageCapture: rotation=$rotation")
Log.i(TAG, "buildImageCapture: resolution=$resolution") Log.i(TAG, "buildImageCapture: resolution=$resolution")
setTargetResolution(resolution) Log.i(TAG, "buildImageCapture: rotatedResolution=$rotatedResolution")
setTargetResolution(rotatedResolution)
} ?: setTargetAspectRatio(aspectRatio) } ?: setTargetAspectRatio(aspectRatio)
} }
.build() .build()
} }
private fun getRotatedResolution(rotationDegrees: Int, resolution: Size): Size {
return if (rotationDegrees == Surface.ROTATION_0 || rotationDegrees == Surface.ROTATION_180) {
Size(resolution.height, resolution.width)
} else {
Size(resolution.width, resolution.height)
}
}
private fun buildPreview(aspectRatio: Int, rotation: Int): Preview { private fun buildPreview(aspectRatio: Int, rotation: Int): Preview {
return Preview.Builder() return Preview.Builder()
.setTargetRotation(rotation) .setTargetRotation(rotation)
@ -211,7 +224,7 @@ class CameraXPreview(
private fun buildVideoCapture(): VideoCapture<Recorder> { private fun buildVideoCapture(): VideoCapture<Recorder> {
val qualitySelector = QualitySelector.from( val qualitySelector = QualitySelector.from(
videoQualityManager.getUserSelectedQuality(cameraSelector), videoQualityManager.getUserSelectedQuality(cameraSelector),
FallbackStrategy.lowerQualityOrHigherThan(Quality.SD), FallbackStrategy.higherQualityOrLowerThan(Quality.SD),
) )
val recorder = Recorder.Builder() val recorder = Recorder.Builder()
.setQualitySelector(qualitySelector) .setQualitySelector(qualitySelector)
@ -350,7 +363,7 @@ class CameraXPreview(
if (bitmap != null) { if (bitmap != null) {
listener.onImageCaptured(bitmap) listener.onImageCaptured(bitmap)
} else { } else {
cameraErrorHandler.handleImageCaptureError(ImageCapture.ERROR_CAPTURE_FAILED) cameraErrorHandler.handleImageCaptureError(ERROR_CAPTURE_FAILED)
} }
} }

View File

@ -5,8 +5,16 @@ import android.util.Size
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
data class MySize(val width: Int, val height: Int) { data class MySize(val width: Int, val height: Int) {
companion object {
private const val ONE_MEGA_PIXEL = 1000000
}
val ratio = width / height.toFloat() val ratio = width / height.toFloat()
val pixels: Int = width * height val pixels: Int = width * height
val megaPixels: String = String.format("%.1f", (width * height.toFloat()) / ONE_MEGA_PIXEL)
fun isSixteenToNine() = ratio == 16 / 9f fun isSixteenToNine() = ratio == 16 / 9f
private fun isFiveToThree() = ratio == 5 / 3f private fun isFiveToThree() = ratio == 5 / 3f

View File

@ -1,254 +1,283 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/settings_scrollview" android:id="@+id/settings_coordinator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent">
<LinearLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/settings_holder" android:id="@+id/settings_app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:orientation="vertical">
<TextView <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/settings_color_customization_label" android:id="@+id/settings_toolbar"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="?attr/actionBarSize"
android:text="@string/color_customization" /> android:background="@color/color_primary"
app:layout_scrollFlags="scroll|enterAlways"
app:title="@string/settings"
app:titleTextAppearance="@style/AppTheme.ActionBar.TitleTextStyle" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_nested_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout
android:id="@+id/settings_color_customization_holder" android:id="@+id/settings_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout <TextView
android:id="@+id/settings_customize_colors_holder" android:id="@+id/settings_color_customization_label"
style="@style/SettingsHolderTextViewOneLinerStyle" style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/ripple_all_corners"> android:text="@string/color_customization" />
<com.simplemobiletools.commons.views.MyTextView <LinearLayout
android:id="@+id/settings_customize_colors_label" android:id="@+id/settings_color_customization_holder"
style="@style/SettingsTextLabelStyle" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_customize_colors_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/customize_colors" /> android:background="@drawable/ripple_all_corners">
</RelativeLayout> <com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_customize_colors_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customize_colors" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/settings_general_settings_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/general_settings" />
<LinearLayout
android:id="@+id/settings_general_settings_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_purchase_thank_you"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/purchase_simple_thank_you" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_english_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_english"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_english_language" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_keep_settings_visible_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_keep_settings_visible"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/keep_settings_visible" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/settings_shutter_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shutter" />
<LinearLayout
android:id="@+id/settings_shutter_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_sound_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_sound"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shutter_sound" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_volume_buttons_as_shutter_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_volume_buttons_as_shutter"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/volume_buttons_as_shutter" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/settings_saving_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/saving_label" />
<LinearLayout
android:id="@+id/settings_saving_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_save_photo_metadata_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_save_photo_metadata"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/save_photo_metadata" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_flip_photos_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_flip_photos"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/flip_front_camera_photos_horizontally" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_save_photos_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_save_photos_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save_photos" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_save_photos"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_save_photos_label"
tools:text="Internal/DCIM" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_photo_quality_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_photo_quality_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/photo_compression_quality" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_photo_quality"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_photo_quality_label"
tools:text="80%" />
</RelativeLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
<TextView </androidx.core.widget.NestedScrollView>
android:id="@+id/settings_general_settings_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/general_settings" />
<LinearLayout </androidx.coordinatorlayout.widget.CoordinatorLayout>
android:id="@+id/settings_general_settings_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_purchase_thank_you"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/purchase_simple_thank_you" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_english_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_english"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_english_language" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_keep_settings_visible_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_keep_settings_visible"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/keep_settings_visible" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/settings_shutter_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shutter" />
<LinearLayout
android:id="@+id/settings_shutter_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_sound_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_sound"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shutter_sound" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_volume_buttons_as_shutter_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_volume_buttons_as_shutter"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/volume_buttons_as_shutter" />
</RelativeLayout>
</LinearLayout>
<TextView
android:id="@+id/settings_saving_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/saving_label" />
<LinearLayout
android:id="@+id/settings_saving_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/medium_margin"
android:background="@drawable/section_holder_stroke"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_save_photo_metadata_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_top_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_save_photo_metadata"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/save_photo_metadata" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_flip_photos_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_flip_photos"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/flip_front_camera_photos_horizontally" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_save_photos_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_save_photos_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save_photos" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_save_photos"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_save_photos_label"
tools:text="Internal/DCIM" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_photo_quality_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_photo_quality_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/photo_compression_quality" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_photo_quality"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_photo_quality_label"
tools:text="80%" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>