From 231a5f9c3fe4137b76921a33d1d6aa13e449c589 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Fri, 15 Jul 2022 21:32:54 +0100 Subject: [PATCH] adjust preview based on selected aspect ratio - set the preview scale type to fitStart - both the preview and image capture would use the same size - some other code cleanups to remove unused code / formatting changes --- .../camera/activities/MainActivity.kt | 23 ++----- .../camera/helpers/Constants.kt | 2 - .../camera/helpers/ImageQualityManager.kt | 44 +++++-------- .../camera/helpers/VideoQualityManager.kt | 5 +- .../camera/implementations/CameraXPreview.kt | 66 ++++++++----------- app/src/main/res/layout/activity_main.xml | 4 +- .../res/layout/dialog_change_resolution.xml | 15 ++--- 7 files changed, 59 insertions(+), 100 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt index a3224934..a85f5d43 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -50,8 +50,13 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera private var mCurrVideoRecTimer = 0 var mLastHandledOrientation = 0 - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { + window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) useDynamicTheme = false super.onCreate(savedInstanceState) appLaunched(BuildConfig.APPLICATION_ID) @@ -62,22 +67,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera supportActionBar?.hide() checkWhatsNewDialog() setupOrientationEventListener() - if (isRPlus()) { - setShowWhenLocked(true) - setTurnScreenOn(true) - window.insetsController?.hide(WindowInsets.Type.statusBars()) - } else if (isOreoMr1Plus()) { - setShowWhenLocked(true) - setTurnScreenOn(true) - window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) - } else { - window.addFlags( - WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or - WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or - WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or - WindowManager.LayoutParams.FLAG_FULLSCREEN - ) - } } override fun onResume() { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt index b36a6ba2..0b543b7c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt @@ -15,8 +15,6 @@ const val FLASHLIGHT_STATE = "flashlight_state" const val INIT_PHOTO_MODE = "init_photo_mode" const val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index_2" const val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index_2" -const val BACK_VIDEO_QUALITY = "back_video_quality_2" -const val FRONT_VIDEO_QUALITY = "front_video_quality_2" const val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index_2" const val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index_2" const val KEEP_SETTINGS_VISIBLE = "keep_settings_visible" diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/ImageQualityManager.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/ImageQualityManager.kt index 8c406f45..b92e5867 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/ImageQualityManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/ImageQualityManager.kt @@ -4,9 +4,6 @@ import android.content.Context import android.graphics.ImageFormat import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager -import android.hardware.camera2.params.StreamConfigurationMap -import android.media.MediaRecorder -import android.util.Size import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.CameraSelector import com.simplemobiletools.camera.extensions.config @@ -19,8 +16,6 @@ class ImageQualityManager( ) { companion object { - private const val MAX_VIDEO_WIDTH = 4096 - private const val MAX_VIDEO_HEIGHT = 2160 private val CAMERA_LENS = arrayOf(CameraCharacteristics.LENS_FACING_FRONT, CameraCharacteristics.LENS_FACING_BACK) } @@ -29,29 +24,25 @@ class ImageQualityManager( private val imageQualities = mutableListOf() fun initSupportedQualities() { - for (cameraId in cameraManager.cameraIdList) { - try { - val characteristics = cameraManager.getCameraCharacteristics(cameraId) - for (lens in CAMERA_LENS) { - if (characteristics.get(CameraCharacteristics.LENS_FACING) == lens) { - val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue - val imageSizes = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) } - val cameraSelector = lens.toCameraSelector() - imageQualities.add(CameraSelectorImageQualities(cameraSelector, imageSizes)) + if (imageQualities.isEmpty()) { + for (cameraId in cameraManager.cameraIdList) { + try { + val characteristics = cameraManager.getCameraCharacteristics(cameraId) + for (lens in CAMERA_LENS) { + if (characteristics.get(CameraCharacteristics.LENS_FACING) == lens) { + val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue + val imageSizes = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) } + val cameraSelector = lens.toCameraSelector() + imageQualities.add(CameraSelectorImageQualities(cameraSelector, imageSizes)) + } } + } catch (e: Exception) { + activity.showErrorToast(e) } - } catch (e: Exception) { - activity.showErrorToast(e) } } } - private fun getAvailableVideoSizes(configMap: StreamConfigurationMap): List { - return configMap.getOutputSizes(MediaRecorder::class.java).filter { - it.width <= MAX_VIDEO_WIDTH && it.height <= MAX_VIDEO_HEIGHT - } - } - private fun Int.toCameraSelector(): CameraSelector { return if (this == CameraCharacteristics.LENS_FACING_FRONT) { CameraSelector.DEFAULT_FRONT_CAMERA @@ -60,20 +51,19 @@ class ImageQualityManager( } } - fun getUserSelectedResolution(cameraSelector: CameraSelector): Size? { + fun getUserSelectedResolution(cameraSelector: CameraSelector): MySize { val index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex return imageQualities.filter { it.camSelector == cameraSelector } .flatMap { it.qualities } - .sortedByDescending { it.pixels } + .sortedWith(compareByDescending { it.ratio }.thenByDescending { it.pixels }) .distinctBy { it.pixels } - .map { Size(it.width, it.height) } - .getOrNull(index) + .filter { it.megaPixels != "0.0" }[index] } fun getSupportedResolutions(cameraSelector: CameraSelector): List { return imageQualities.filter { it.camSelector == cameraSelector } .flatMap { it.qualities } - .sortedByDescending { it.pixels } + .sortedWith(compareByDescending { it.ratio }.thenByDescending { it.pixels }) .distinctBy { it.pixels } .filter { it.megaPixels != "0.0" } } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt index 7243fc7d..0a3d784a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt @@ -6,7 +6,6 @@ import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import com.simplemobiletools.camera.extensions.config -import com.simplemobiletools.camera.extensions.toCameraXQuality import com.simplemobiletools.camera.extensions.toVideoQuality import com.simplemobiletools.camera.models.CameraSelectorVideoQualities import com.simplemobiletools.camera.models.VideoQuality @@ -45,10 +44,10 @@ class VideoQualityManager( } } - fun getUserSelectedQuality(cameraSelector: CameraSelector): Quality { + fun getUserSelectedQuality(cameraSelector: CameraSelector): VideoQuality { var selectionIndex = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontVideoResIndex else config.backVideoResIndex selectionIndex = selectionIndex.coerceAtLeast(0) - return getSupportedQualities(cameraSelector).getOrElse(selectionIndex) { VideoQuality.HD }.toCameraXQuality() + return getSupportedQualities(cameraSelector).getOrElse(selectionIndex) { VideoQuality.HD } } fun getSupportedQualities(cameraSelector: CameraSelector): List { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt index b14d4af3..42a6927f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt @@ -18,7 +18,6 @@ import androidx.core.content.ContextCompat import androidx.core.view.doOnLayout import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import androidx.window.layout.WindowMetricsCalculator import com.bumptech.glide.load.ImageHeaderParser.UNKNOWN_ORIENTATION import com.simplemobiletools.camera.R import com.simplemobiletools.camera.dialogs.ChangeResolutionDialogX @@ -26,11 +25,9 @@ import com.simplemobiletools.camera.extensions.* import com.simplemobiletools.camera.helpers.* import com.simplemobiletools.camera.interfaces.MyPreview import com.simplemobiletools.camera.models.MediaOutput +import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.helpers.ensureBackgroundThread -import kotlin.math.abs -import kotlin.math.max -import kotlin.math.min class CameraXPreview( private val activity: AppCompatActivity, @@ -42,9 +39,6 @@ class CameraXPreview( ) : MyPreview, DefaultLifecycleObserver { companion object { - private const val RATIO_4_3_VALUE = 4.0 / 3.0 - private const val RATIO_16_9_VALUE = 16.0 / 9.0 - // Auto focus is 1/6 of the area. private const val AF_SIZE = 1.0f / 6.0f private const val AE_SIZE = AF_SIZE * 1.5f @@ -55,7 +49,6 @@ class CameraXPreview( private val mainExecutor = ContextCompat.getMainExecutor(activity) private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager private val mediaSoundHelper = MediaSoundHelper() - private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate() private val videoQualityManager = VideoQualityManager(activity) private val imageQualityManager = ImageQualityManager(activity) private val exifRemover = ExifRemover(contentResolver) @@ -73,9 +66,13 @@ class CameraXPreview( in 225 until 315 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } - preview?.targetRotation = rotation - imageCapture?.targetRotation = rotation - videoCapture?.targetRotation = rotation + + if (lastRotation != rotation) { + preview?.targetRotation = rotation + imageCapture?.targetRotation = rotation + videoCapture?.targetRotation = rotation + lastRotation = rotation + } } } @@ -89,6 +86,7 @@ class CameraXPreview( private var cameraSelector = config.lastUsedCameraLens.toCameraSelector() private var flashMode = FLASH_MODE_OFF private var isPhotoCapture = initInPhotoMode + private var lastRotation = 0 init { bindToLifeCycle() @@ -122,17 +120,18 @@ class CameraXPreview( private fun bindCameraUseCases() { val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.") - val metrics = windowMetricsCalculator.computeCurrentWindowMetrics(activity).bounds - val aspectRatio = if (isPhotoCapture) { - aspectRatio(metrics.width(), metrics.height()) + val rotation = previewView.display.rotation + val resolution = if (isPhotoCapture) { + imageQualityManager.getUserSelectedResolution(cameraSelector) } else { val selectedQuality = videoQualityManager.getUserSelectedQuality(cameraSelector) - selectedQuality.getAspectRatio() + MySize(selectedQuality.width, selectedQuality.height) } - val rotation = previewView.display.rotation - preview = buildPreview(aspectRatio, rotation) - val captureUseCase = getCaptureUseCase(aspectRatio, rotation) + val rotatedResolution = getRotatedResolution(resolution, rotation) + + preview = buildPreview(rotatedResolution, rotation) + val captureUseCase = getCaptureUseCase(rotatedResolution, rotation) cameraProvider.unbindAll() camera = cameraProvider.bindToLifecycle( activity, @@ -167,10 +166,10 @@ class CameraXPreview( } } - private fun getCaptureUseCase(aspectRatio: Int, rotation: Int): UseCase { + private fun getCaptureUseCase(resolution: Size, rotation: Int): UseCase { return if (isPhotoCapture) { cameraProvider?.unbind(videoCapture) - buildImageCapture(aspectRatio, rotation).also { + buildImageCapture(resolution, rotation).also { imageCapture = it } } else { @@ -181,22 +180,17 @@ class CameraXPreview( } } - private fun buildImageCapture(aspectRatio: Int, rotation: Int): ImageCapture { + private fun buildImageCapture(resolution: Size, rotation: Int): ImageCapture { return Builder() .setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY) .setFlashMode(flashMode) .setJpegQuality(config.photoQuality) .setTargetRotation(rotation) - .apply { - imageQualityManager.getUserSelectedResolution(cameraSelector)?.let { resolution -> - val rotatedResolution = getRotatedResolution(rotation, resolution) - setTargetResolution(rotatedResolution) - } ?: setTargetAspectRatio(aspectRatio) - } + .setTargetResolution(resolution) .build() } - private fun getRotatedResolution(rotationDegrees: Int, resolution: Size): Size { + private fun getRotatedResolution(resolution: MySize, rotationDegrees: Int): Size { return if (rotationDegrees == Surface.ROTATION_0 || rotationDegrees == Surface.ROTATION_180) { Size(resolution.height, resolution.width) } else { @@ -204,16 +198,16 @@ class CameraXPreview( } } - private fun buildPreview(aspectRatio: Int, rotation: Int): Preview { + private fun buildPreview(resolution: Size, rotation: Int): Preview { return Preview.Builder() .setTargetRotation(rotation) - .setTargetAspectRatio(aspectRatio) + .setTargetResolution(resolution) .build() } private fun buildVideoCapture(): VideoCapture { val qualitySelector = QualitySelector.from( - videoQualityManager.getUserSelectedQuality(cameraSelector), + videoQualityManager.getUserSelectedQuality(cameraSelector).toCameraXQuality(), FallbackStrategy.higherQualityOrLowerThan(Quality.SD), ) val recorder = Recorder.Builder() @@ -222,14 +216,6 @@ class CameraXPreview( return VideoCapture.withOutput(recorder) } - private fun aspectRatio(width: Int, height: Int): Int { - val previewRatio = max(width, height).toDouble() / min(width, height) - if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) { - return AspectRatio.RATIO_4_3 - } - return AspectRatio.RATIO_16_9 - } - private fun hasBackCamera(): Boolean { return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false } @@ -288,7 +274,7 @@ class CameraXPreview( activity, isFrontCameraInUse(), imageQualityManager.getSupportedResolutions(cameraSelector), - videoQualityManager.getSupportedQualities(cameraSelector) + videoQualityManager.getSupportedQualities(cameraSelector), ) { if (oldQuality != videoQualityManager.getUserSelectedQuality(cameraSelector)) { currentRecording?.stop() diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 0b5ac725..7ed73b50 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,6 @@ + android:layout_height="match_parent" + app:scaleType="fitStart" /> - + android:text="@string/photo" /> + android:clickable="false" /> @@ -48,17 +45,15 @@ android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toStartOf="@+id/change_resolution_video" - android:layout_toLeftOf="@+id/change_resolution_video" - android:text="@string/video"/> + android:text="@string/video" /> + android:clickable="false" />