From a2828b69c4145fade8cbb8878487d331ba4a8757 Mon Sep 17 00:00:00 2001 From: tibbi Date: Thu, 6 Oct 2022 19:31:50 +0200 Subject: [PATCH] removing some no longer needed stuff --- .../camera/activities/MainActivity.kt | 14 +- .../camera/dialogs/ChangeResolutionDialog.kt | 95 -- .../camera/dialogs/ChangeResolutionDialogX.kt | 103 -- .../camera/extensions/Int.kt | 9 - .../camera/extensions/Quality.kt | 9 - .../camera/helpers/Constants.kt | 12 - .../camera/helpers/MediaSizeStore.kt | 4 +- .../camera/helpers/TabSelectedListener.kt | 2 +- .../camera/helpers/VideoQualityManager.kt | 1 - .../camera/implementations/CameraXPreview.kt | 16 - .../camera/implementations/MyCameraImpl.kt | 20 - .../camera/interfaces/MyPreview.kt | 8 - .../camera/models/FocusArea.kt | 5 - .../simplemobiletools/camera/models/MySize.kt | 3 - .../camera/views/AutoFitTextureView.kt | 38 - .../camera/views/CameraPreview.kt | 1004 ----------------- .../res/layout/dialog_change_resolution.xml | 59 - 17 files changed, 10 insertions(+), 1392 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialog.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/implementations/MyCameraImpl.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/models/FocusArea.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/views/AutoFitTextureView.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/views/CameraPreview.kt delete mode 100644 app/src/main/res/layout/dialog_change_resolution.xml 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 0acb4542..38e9ccc2 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.res.ColorStateList import android.graphics.Bitmap import android.hardware.SensorManager +import android.hardware.camera2.CameraCharacteristics import android.net.Uri import android.os.Bundle import android.os.Handler @@ -31,7 +32,6 @@ import com.simplemobiletools.camera.extensions.toFlashModeId import com.simplemobiletools.camera.helpers.* import com.simplemobiletools.camera.implementations.CameraXInitializer import com.simplemobiletools.camera.implementations.CameraXPreviewListener -import com.simplemobiletools.camera.implementations.MyCameraImpl import com.simplemobiletools.camera.interfaces.MyPreview import com.simplemobiletools.camera.models.ResolutionOption import com.simplemobiletools.camera.views.FocusCircleView @@ -55,7 +55,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera private lateinit var flashModeScene: Scene private lateinit var mOrientationEventListener: OrientationEventListener private lateinit var mFocusCircleView: FocusCircleView - private lateinit var mCameraImpl: MyCameraImpl private var mPreview: MyPreview? = null private var mediaSizeToggleGroup: MaterialButtonToggleGroup? = null private var mPreviewUri: Uri? = null @@ -181,8 +180,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera mIsHardwareShutterHandled = false mCurrVideoRecTimer = 0 mLastHandledOrientation = 0 - mCameraImpl = MyCameraImpl(applicationContext) - config.lastUsedCamera = mCameraImpl.getBackCameraId().toString() + config.lastUsedCamera = CameraCharacteristics.LENS_FACING_BACK.toString() } override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { @@ -330,8 +328,12 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera checkImageCaptureIntent() mPreview?.setIsImageCaptureIntent(isImageCaptureIntent()) - val imageDrawable = - if (config.lastUsedCamera == mCameraImpl.getBackCameraId().toString()) R.drawable.ic_camera_front_vector else R.drawable.ic_camera_rear_vector + val imageDrawable = if (config.lastUsedCamera == CameraCharacteristics.LENS_FACING_BACK.toString()) { + R.drawable.ic_camera_front_vector + } else { + R.drawable.ic_camera_rear_vector + } + toggle_camera.setImageResource(imageDrawable) mFocusCircleView = FocusCircleView(applicationContext) diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialog.kt b/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialog.kt deleted file mode 100644 index ca6fb6b9..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialog.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.simplemobiletools.camera.dialogs - -import android.view.LayoutInflater -import android.view.View -import androidx.appcompat.app.AlertDialog -import com.simplemobiletools.camera.R -import com.simplemobiletools.camera.activities.SimpleActivity -import com.simplemobiletools.camera.extensions.config -import com.simplemobiletools.camera.models.MySize -import com.simplemobiletools.commons.dialogs.RadioGroupDialog -import com.simplemobiletools.commons.extensions.getAlertDialogBuilder -import com.simplemobiletools.commons.extensions.setupDialogStuff -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_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, - val videoResolutions: ArrayList, val openVideoResolutions: Boolean, val callback: () -> Unit -) { - private var dialog: AlertDialog? = null - private val config = activity.config - - init { - val view = LayoutInflater.from(activity).inflate(R.layout.dialog_change_resolution, null).apply { - setupPhotoResolutionPicker(this) - setupVideoResolutionPicker(this) - } - - activity.getAlertDialogBuilder() - .setPositiveButton(R.string.ok, null) - .setOnDismissListener { callback() } - .apply { - val titleId = if (isFrontCamera) R.string.front_camera else R.string.back_camera - activity.setupDialogStuff(view, this, titleId) { alertDialog -> - dialog = alertDialog - if (openVideoResolutions) { - view.change_resolution_video_holder.performClick() - } - } - } - } - - private fun setupPhotoResolutionPicker(view: View) { - val items = getFormattedResolutions(photoResolutions) - var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex - selectionIndex = Math.max(selectionIndex, 0) - - view.change_resolution_photo_holder.setOnClickListener { - RadioGroupDialog(activity, items, selectionIndex) { - selectionIndex = it as Int - view.change_resolution_photo.text = items[selectionIndex].title - if (isFrontCamera) { - config.frontPhotoResIndex = it - } else { - config.backPhotoResIndex = it - } - dialog?.dismiss() - } - } - view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title - } - - private fun setupVideoResolutionPicker(view: View) { - val items = getFormattedResolutions(videoResolutions) - var selectionIndex = if (isFrontCamera) config.frontVideoResIndex else config.backVideoResIndex - - view.change_resolution_video_holder.setOnClickListener { - RadioGroupDialog(activity, items, selectionIndex) { - selectionIndex = it as Int - view.change_resolution_video.text = items[selectionIndex].title - if (isFrontCamera) { - config.frontVideoResIndex = it - } else { - config.backVideoResIndex = it - } - dialog?.dismiss() - } - } - view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title - } - - private fun getFormattedResolutions(resolutions: List): ArrayList { - val items = ArrayList(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 - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt b/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt deleted file mode 100644 index cc04517e..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.simplemobiletools.camera.dialogs - -import android.app.Activity -import android.view.LayoutInflater -import android.view.View -import androidx.appcompat.app.AlertDialog -import com.simplemobiletools.camera.R -import com.simplemobiletools.camera.extensions.config -import com.simplemobiletools.camera.models.MySize -import com.simplemobiletools.camera.models.VideoQuality -import com.simplemobiletools.commons.dialogs.RadioGroupDialog -import com.simplemobiletools.commons.extensions.getAlertDialogBuilder -import com.simplemobiletools.commons.extensions.setupDialogStuff -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_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 ChangeResolutionDialogX( - private val activity: Activity, - private val isFrontCamera: Boolean, - private val photoResolutions: List = listOf(), - private val videoResolutions: List, - private val callback: () -> Unit, -) { - private var dialog: AlertDialog? = null - private val config = activity.config - - init { - val view = LayoutInflater.from(activity).inflate(R.layout.dialog_change_resolution, null).apply { - setupPhotoResolutionPicker(this) - setupVideoResolutionPicker(this) - } - - activity.getAlertDialogBuilder() - .setPositiveButton(R.string.ok, null) - .apply { - 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) { - val items = photoResolutions.mapIndexed { index, resolution -> - val megapixels = resolution.megaPixels - val aspectRatio = resolution.getAspectRatio(activity) - if (resolution.isFullScreen) { - //TODO: Extract to string resource - RadioItem(index, "Full") - } else { - RadioItem(index, "${resolution.width} x ${resolution.height} ($megapixels MP, $aspectRatio)") - } - } - var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex - selectionIndex = selectionIndex.coerceAtLeast(0) - - view.change_resolution_photo_holder.setOnClickListener { - RadioGroupDialog(activity, ArrayList(items), selectionIndex) { - selectionIndex = it as Int - view.change_resolution_photo.text = items[selectionIndex].title - if (isFrontCamera) { - config.frontPhotoResIndex = it - } else { - config.backPhotoResIndex = it - } - dialog?.dismiss() - callback.invoke() - } - } - view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title - } - - private fun setupVideoResolutionPicker(view: View) { - val items = videoResolutions.mapIndexed { index, videoQuality -> - val megapixels = videoQuality.megaPixels - val aspectRatio = videoQuality.getAspectRatio(activity) - RadioItem(index, "${videoQuality.width} x ${videoQuality.height} ($megapixels MP, $aspectRatio)") - } - - var selectionIndex = if (isFrontCamera) config.frontVideoResIndex else config.backVideoResIndex - selectionIndex = selectionIndex.coerceAtLeast(0) - - view.change_resolution_video_holder.setOnClickListener { - RadioGroupDialog(activity, ArrayList(items), selectionIndex) { - selectionIndex = it as Int - val selectedItem = items[selectionIndex] - view.change_resolution_video.text = selectedItem.title - if (isFrontCamera) { - config.frontVideoResIndex = selectionIndex - } else { - config.backVideoResIndex = selectionIndex - } - dialog?.dismiss() - callback.invoke() - } - } - val selectedItem = items.getOrNull(selectionIndex) - view.change_resolution_video.text = selectedItem?.title - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Int.kt b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Int.kt index 6f13a1ff..78767ee3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Int.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Int.kt @@ -34,15 +34,6 @@ fun Int.toFlashModeId(): Int { } } -fun Int.idToAppFlashMode(): Int { - return when (this) { - R.id.flash_on -> FLASH_ON - R.id.flash_off -> FLASH_OFF - R.id.flash_auto -> FLASH_AUTO - else -> throw IllegalArgumentException("Unknown mode: $this") - } -} - fun Int.toCameraSelector(): CameraSelector { return if (this == CameraSelector.LENS_FACING_FRONT) { CameraSelector.DEFAULT_FRONT_CAMERA diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Quality.kt b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Quality.kt index 017f6c01..07f4b78e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Quality.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/Quality.kt @@ -1,6 +1,5 @@ package com.simplemobiletools.camera.extensions -import androidx.camera.core.AspectRatio import androidx.camera.video.Quality import com.simplemobiletools.camera.models.VideoQuality @@ -22,11 +21,3 @@ fun VideoQuality.toCameraXQuality(): Quality { VideoQuality.SD -> Quality.SD } } - -fun Quality.getAspectRatio(): Int { - return when(this) { - Quality.UHD, Quality.FHD, Quality.HD -> AspectRatio.RATIO_16_9 - Quality.SD -> AspectRatio.RATIO_4_3 - else -> throw IllegalArgumentException("Unsupported quality: $this") - } -} 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 0b543b7c..928da8b6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/Constants.kt @@ -17,7 +17,6 @@ const val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index_2" const val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index_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" const val SAVE_PHOTO_METADATA = "save_photo_metadata" const val PHOTO_QUALITY = "photo_quality" @@ -25,17 +24,6 @@ const val FLASH_OFF = 0 const val FLASH_ON = 1 const val FLASH_AUTO = 2 -// camera states -const val STATE_INIT = 0 -const val STATE_PREVIEW = 1 -const val STATE_PICTURE_TAKEN = 2 -const val STATE_WAITING_LOCK = 3 -const val STATE_WAITING_PRECAPTURE = 4 -const val STATE_WAITING_NON_PRECAPTURE = 5 -const val STATE_STARTING_RECORDING = 6 -const val STATE_STOPING_RECORDING = 7 -const val STATE_RECORDING = 8 - fun compensateDeviceRotation(orientation: Int) = when (orientation) { ORIENT_LANDSCAPE_LEFT -> 270 ORIENT_LANDSCAPE_RIGHT -> 90 diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaSizeStore.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaSizeStore.kt index 8702eb66..dd3f9762 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaSizeStore.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaSizeStore.kt @@ -1,8 +1,6 @@ package com.simplemobiletools.camera.helpers -class MediaSizeStore( - private val config: Config, -) { +class MediaSizeStore(private val config: Config) { fun storeSize(isPhotoCapture: Boolean, isFrontCamera: Boolean, currentIndex: Int) { if (isPhotoCapture) { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt index 361729ec..64daa70c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt @@ -3,7 +3,7 @@ package com.simplemobiletools.camera.helpers import com.google.android.material.tabs.TabLayout interface TabSelectedListener : TabLayout.OnTabSelectedListener { - override fun onTabReselected(tab: TabLayout.Tab?){} + override fun onTabReselected(tab: TabLayout.Tab?) {} override fun onTabUnselected(tab: TabLayout.Tab?) {} } 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 d768deec..7a864f97 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt @@ -14,7 +14,6 @@ import com.simplemobiletools.commons.extensions.showErrorToast class VideoQualityManager( private val activity: AppCompatActivity, ) { - companion object { private val QUALITIES = listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD) private val CAMERA_SELECTORS = arrayOf(CameraSelector.DEFAULT_BACK_CAMERA, CameraSelector.DEFAULT_FRONT_CAMERA) 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 0b77a5f0..b18eaedb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt @@ -23,7 +23,6 @@ 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 import com.simplemobiletools.camera.extensions.* import com.simplemobiletools.camera.helpers.* import com.simplemobiletools.camera.interfaces.MyPreview @@ -326,21 +325,6 @@ class CameraXPreview( orientationEventListener.disable() } - override fun showChangeResolutionDialog() { - val oldQuality = videoQualityManager.getUserSelectedQuality(cameraSelector) - ChangeResolutionDialogX( - activity, - isFrontCameraInUse(), - imageQualityManager.getSupportedResolutions(cameraSelector), - videoQualityManager.getSupportedQualities(cameraSelector), - ) { - if (oldQuality != videoQualityManager.getUserSelectedQuality(cameraSelector)) { - currentRecording?.stop() - } - startCamera() - } - } - override fun showChangeResolution() { val selectedResolution = if (isPhotoCapture) { imageQualityManager.getUserSelectedResolution(cameraSelector).toResolutionOption() diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/MyCameraImpl.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/MyCameraImpl.kt deleted file mode 100644 index ae93e95e..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/MyCameraImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.simplemobiletools.camera.implementations - -import android.content.Context -import android.hardware.camera2.CameraCharacteristics -import android.hardware.camera2.CameraManager - -class MyCameraImpl(val context: Context) { - fun getFrontCameraId() = CameraCharacteristics.LENS_FACING_FRONT - - fun getBackCameraId() = CameraCharacteristics.LENS_FACING_BACK - - fun getCountOfCameras(): Int? { - return try { - val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager - manager.cameraIdList.size - } catch (e: Exception) { - null - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/interfaces/MyPreview.kt b/app/src/main/kotlin/com/simplemobiletools/camera/interfaces/MyPreview.kt index cc670165..02447fc4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/interfaces/MyPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/interfaces/MyPreview.kt @@ -4,20 +4,12 @@ import android.net.Uri interface MyPreview { - fun onResumed() = Unit - - fun onPaused() = Unit - fun setTargetUri(uri: Uri) = Unit fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean) = Unit fun setFlashlightState(state: Int) = Unit - fun getCameraState(): Int = 0 - - fun showChangeResolutionDialog() - fun toggleFrontBackCamera() fun toggleFlashlight() = Unit diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/models/FocusArea.kt b/app/src/main/kotlin/com/simplemobiletools/camera/models/FocusArea.kt deleted file mode 100644 index f4d631c2..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/models/FocusArea.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.simplemobiletools.camera.models - -import android.graphics.Rect - -data class FocusArea(val rect: Rect, val weight: Int) diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/models/MySize.kt b/app/src/main/kotlin/com/simplemobiletools/camera/models/MySize.kt index da86e6e4..4201aa8d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/models/MySize.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/models/MySize.kt @@ -1,7 +1,6 @@ package com.simplemobiletools.camera.models import android.content.Context -import android.util.Size import androidx.annotation.DrawableRes import androidx.annotation.IdRes import com.simplemobiletools.camera.R @@ -84,6 +83,4 @@ data class MySize(val width: Int, val height: Int, val isFullScreen: Boolean = f fun toResolutionOption(): ResolutionOption { return ResolutionOption(buttonViewId = getButtonId(), imageDrawableResId = getImageResId()) } - - fun toSize() = Size(width, height) } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/views/AutoFitTextureView.kt b/app/src/main/kotlin/com/simplemobiletools/camera/views/AutoFitTextureView.kt deleted file mode 100644 index 6e2399c4..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/views/AutoFitTextureView.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.simplemobiletools.camera.views - -import android.content.Context -import android.util.AttributeSet -import android.view.TextureView - -// taken from the official Camera2 sample at https://github.com/googlesamples/android-Camera2Basic -class AutoFitTextureView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : TextureView(context, attrs, defStyle) { - - private var mRatioWidth = 0 - private var mRatioHeight = 0 - - fun setAspectRatio(width: Int, height: Int) { - if (width < 0 || height < 0) { - throw IllegalArgumentException("Size cannot be negative.") - } - - mRatioWidth = width - mRatioHeight = height - requestLayout() - } - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - val width = MeasureSpec.getSize(widthMeasureSpec) - val height = MeasureSpec.getSize(heightMeasureSpec) - - if (mRatioWidth == 0 || mRatioHeight == 0) { - setMeasuredDimension(width, height) - } else { - if (width < height * mRatioWidth / mRatioHeight) { - setMeasuredDimension(width, width * mRatioHeight / mRatioWidth) - } else { - setMeasuredDimension(height * mRatioWidth / mRatioHeight, height) - } - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/views/CameraPreview.kt b/app/src/main/kotlin/com/simplemobiletools/camera/views/CameraPreview.kt deleted file mode 100644 index 8aaeb856..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/views/CameraPreview.kt +++ /dev/null @@ -1,1004 +0,0 @@ -package com.simplemobiletools.camera.views - -import android.annotation.SuppressLint -import android.content.Context -import android.graphics.ImageFormat -import android.graphics.Matrix -import android.graphics.Rect -import android.graphics.SurfaceTexture -import android.hardware.camera2.* -import android.hardware.camera2.params.MeteringRectangle -import android.hardware.camera2.params.StreamConfigurationMap -import android.media.ImageReader -import android.media.MediaRecorder -import android.net.Uri -import android.os.Handler -import android.os.HandlerThread -import android.util.DisplayMetrics -import android.util.Range -import android.util.Size -import android.util.SparseIntArray -import android.view.* -import android.widget.Toast -import com.simplemobiletools.camera.R -import com.simplemobiletools.camera.activities.MainActivity -import com.simplemobiletools.camera.dialogs.ChangeResolutionDialog -import com.simplemobiletools.camera.extensions.config -import com.simplemobiletools.camera.extensions.getOutputMediaFile -import com.simplemobiletools.camera.helpers.* -import com.simplemobiletools.camera.interfaces.MyPreview -import com.simplemobiletools.camera.models.FocusArea -import com.simplemobiletools.camera.models.MySize -import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.models.FileDirItem -import java.io.File -import java.util.* -import java.util.concurrent.Semaphore -import java.util.concurrent.TimeUnit - -// based on the Android Camera2 photo sample at https://github.com/googlesamples/android-Camera2Basic -// and video sample at https://github.com/googlesamples/android-Camera2Video -class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview { - private val FOCUS_TAG = "focus_tag" - private val MAX_PREVIEW_WIDTH = 1920 - private val MAX_PREVIEW_HEIGHT = 1080 - private val MAX_VIDEO_WIDTH = 4096 - private val MAX_VIDEO_HEIGHT = 2160 - - private val DEFAULT_ORIENTATIONS = SparseIntArray(4).apply { - append(Surface.ROTATION_0, 90) - append(Surface.ROTATION_90, 0) - append(Surface.ROTATION_180, 270) - append(Surface.ROTATION_270, 180) - } - - private val INVERSE_ORIENTATIONS = SparseIntArray(4).apply { - append(Surface.ROTATION_0, 270) - append(Surface.ROTATION_90, 180) - append(Surface.ROTATION_180, 90) - append(Surface.ROTATION_270, 0) - } - - private lateinit var mActivity: MainActivity - private lateinit var mTextureView: AutoFitTextureView - - private var mSensorOrientation = 0 - private var mRotationAtCapture = 0 - private var mZoomLevel = 1f - private var mZoomFingerSpacing = 0 - private var mMaxZoomLevel = 1f - private var mLastFocusX = 0f - private var mLastFocusY = 0f - private var mIsFlashSupported = true - private var mIsZoomSupported = true - private var mIsFocusSupported = true - private var mIsImageCaptureIntent = false - private var mIsInVideoMode = false - private var mIsRecording = false - private var mUseFrontCamera = false - private var mCameraId = "" - private var mLastVideoPath = "" - private var mCameraState = STATE_INIT - private var mFlashlightState = FLASH_OFF - - private var mBackgroundThread: HandlerThread? = null - private var mBackgroundHandler: Handler? = null - private var mImageReader: ImageReader? = null - private var mPreviewSize: Size? = null - private var mTargetUri: Uri? = null - private var mCameraDevice: CameraDevice? = null - private var mCameraCharacteristics: CameraCharacteristics? = null - private var mCaptureSession: CameraCaptureSession? = null - private var mPreviewRequestBuilder: CaptureRequest.Builder? = null - private var mPreviewRequest: CaptureRequest? = null - private var mMediaRecorder: MediaRecorder? = null - private val mCameraToPreviewMatrix = Matrix() - private val mPreviewToCameraMatrix = Matrix() - private val mCameraOpenCloseLock = Semaphore(1) - private val mediaSoundHelper = MediaSoundHelper() - private var mZoomRect: Rect? = null - - constructor(context: Context) : super(context) - - @SuppressLint("ClickableViewAccessibility") - constructor(activity: MainActivity, textureView: AutoFitTextureView, initPhotoMode: Boolean) : super(activity) { - mActivity = activity - mTextureView = textureView - val cameraCharacteristics = try { - getCameraCharacteristics(activity.config.lastUsedCamera) - } catch (e: Exception) { - mActivity.showErrorToast("Get camera characteristics $e") - null - } - - mUseFrontCamera = false - mIsInVideoMode = !initPhotoMode - mediaSoundHelper.loadSounds() - - val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { - override fun onSingleTapConfirmed(event: MotionEvent): Boolean { - if (mIsFocusSupported && mCaptureSession != null) { - focusArea(event.rawX, event.rawY, true) - } - return true - } - }) - - mTextureView.setOnTouchListener { view, event -> - gestureDetector.onTouchEvent(event) - - if (mIsZoomSupported && event.pointerCount > 1 && mCaptureSession != null) { - try { - handleZoom(event) - } catch (e: Exception) { - } - } - true - } - } - - override fun onResumed() { - startBackgroundThread() - - if (mTextureView.isAvailable) { - openCamera(mTextureView.width, mTextureView.height) - } else { - mTextureView.surfaceTextureListener = this - } - } - - override fun onPaused() { - closeCamera() - stopBackgroundThread() - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { - closeCamera() - openCamera(width, height) - } - - override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} - - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture) = true - - override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { - openCamera(width, height) - } - - private fun startBackgroundThread() { - mBackgroundThread = HandlerThread("SimpleCameraBackground") - mBackgroundThread!!.start() - mBackgroundHandler = Handler(mBackgroundThread!!.looper) - } - - private fun stopBackgroundThread() { - try { - mBackgroundThread?.quitSafely() - mBackgroundThread?.join() - mBackgroundThread = null - mBackgroundHandler = null - } catch (e: InterruptedException) { - } - } - - @SuppressLint("MissingPermission") - private fun openCamera(width: Int, height: Int) { - try { - mActivity.runOnUiThread { - setupCameraOutputs(width, height) - if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { - throw RuntimeException("Time out waiting to lock camera opening.") - } - getCameraManager().openCamera(mCameraId, cameraStateCallback, mBackgroundHandler) - } - } catch (e: Exception) { - mActivity.showErrorToast("Open camera $e") - } - } - - private fun closeCamera() { - try { - mCameraOpenCloseLock.acquire() - mCaptureSession?.close() - mCaptureSession = null - mCameraDevice?.close() - mCameraDevice = null - mImageReader?.close() - mImageReader = null - mMediaRecorder?.release() - mMediaRecorder = null - } catch (e: Exception) { - } finally { - mCameraOpenCloseLock.release() - } - } - - private fun closeCaptureSession() { - mCaptureSession?.close() - mCaptureSession = null - } - - private fun handleZoom(event: MotionEvent) { - val sensorRect = mCameraCharacteristics!!.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE) ?: return - val currentFingerSpacing = getFingerSpacing(event) - - var delta = 0.05f - if (mZoomFingerSpacing != 0) { - if (currentFingerSpacing > mZoomFingerSpacing) { - if (mMaxZoomLevel - mZoomLevel <= delta) { - delta = mMaxZoomLevel - mZoomLevel - } - mZoomLevel += delta - } else if (currentFingerSpacing < mZoomFingerSpacing) { - if (mZoomLevel - delta < 1f) { - delta = mZoomLevel - 1f - } - mZoomLevel -= delta - } - - val ratio = 1 / mZoomLevel - val croppedWidth = sensorRect.width() - Math.round(sensorRect.width() * ratio) - val croppedHeight = sensorRect.height() - Math.round(sensorRect.height() * ratio) - mZoomRect = Rect(croppedWidth / 2, croppedHeight / 2, sensorRect.width() - croppedWidth / 2, sensorRect.height() - croppedHeight / 2) - mPreviewRequestBuilder!!.set(CaptureRequest.SCALER_CROP_REGION, mZoomRect) - mPreviewRequest = mPreviewRequestBuilder!!.build() - mCaptureSession!!.setRepeatingRequest(mPreviewRequest!!, mCaptureCallback, mBackgroundHandler) - } - mZoomFingerSpacing = currentFingerSpacing.toInt() - } - - private fun getFingerSpacing(event: MotionEvent): Float { - val x = event.getX(0) - event.getX(1) - val y = event.getY(0) - event.getY(1) - return Math.sqrt((x * x + y * y).toDouble()).toFloat() - } - - private val imageAvailableListener = ImageReader.OnImageAvailableListener { reader -> - try { - val image = reader.acquireNextImage() - val buffer = image.planes.first().buffer - val bytes = ByteArray(buffer.remaining()) - buffer.get(bytes) - image.close() - PhotoProcessor(mActivity, mTargetUri, mRotationAtCapture, mSensorOrientation, mUseFrontCamera, mIsImageCaptureIntent).execute(bytes) - } catch (e: Exception) { - } - } - - private fun getCurrentResolution(): MySize { - val configMap = mCameraCharacteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: return MySize(0, 0) - val resIndex = if (mUseFrontCamera) { - if (mIsInVideoMode) { - mActivity.config.frontVideoResIndex - } else { - mActivity.config.frontPhotoResIndex - } - } else { - if (mIsInVideoMode) { - mActivity.config.backVideoResIndex - } else { - mActivity.config.backPhotoResIndex - } - } - - val outputSizes = if (mIsInVideoMode) { - getAvailableVideoSizes(configMap).toTypedArray() - } else { - configMap.getOutputSizes(ImageFormat.JPEG) - } - - val size = outputSizes.sortedByDescending { it.width * it.height }[resIndex] - return MySize(size.width, size.height) - } - - private fun setupCameraOutputs(width: Int, height: Int) { - val manager = getCameraManager() - try { - for (cameraId in manager.cameraIdList) { - val characteristics = getCameraCharacteristics(cameraId) - - val facing = characteristics.get(CameraCharacteristics.LENS_FACING) ?: continue - if ((mUseFrontCamera && facing == CameraCharacteristics.LENS_FACING_BACK) || (!mUseFrontCamera && facing == CameraCharacteristics.LENS_FACING_FRONT)) { - continue - } - - mCameraId = cameraId - mCameraCharacteristics = characteristics - mMaxZoomLevel = mCameraCharacteristics?.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: return - mZoomLevel = 1f - - mActivity.config.lastUsedCamera = mCameraId - val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! - val currentResolution = getCurrentResolution() - - if (mIsInVideoMode) { - mImageReader = null - mMediaRecorder = MediaRecorder() - } else { - mImageReader = ImageReader.newInstance(currentResolution.width, currentResolution.height, ImageFormat.JPEG, 2) - mImageReader!!.setOnImageAvailableListener(imageAvailableListener, null) - mMediaRecorder = null - } - - val displaySize = getRealDisplaySize() - var maxPreviewWidth = displaySize.width - var maxPreviewHeight = displaySize.height - var rotatedPreviewWidth = width - var rotatedPreviewHeight = height - - mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! - if (mSensorOrientation == 90 || mSensorOrientation == 270) { - rotatedPreviewWidth = height - rotatedPreviewHeight = width - - val tmpWidth = maxPreviewWidth - maxPreviewWidth = maxPreviewHeight - maxPreviewHeight = tmpWidth - } - - if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { - maxPreviewWidth = MAX_PREVIEW_WIDTH - } - - if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { - maxPreviewHeight = MAX_PREVIEW_HEIGHT - } - - val outputSizes = if (mIsInVideoMode) { - getAvailableVideoSizes(configMap).toTypedArray() - } else { - configMap.getOutputSizes(SurfaceTexture::class.java) - } - - mPreviewSize = - chooseOptimalPreviewSize(outputSizes, rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, currentResolution) - - mActivity.runOnUiThread { - mTextureView.setAspectRatio(mPreviewSize!!.height, mPreviewSize!!.width) - } - characteristics.apply { - mIsFlashSupported = get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false - mIsZoomSupported = get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: 0f > 0f - mIsFocusSupported = get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)!!.size > 1 - } - mActivity.setFlashAvailable(mIsFlashSupported) - mActivity.onChangeCamera(mUseFrontCamera) - return - } - } catch (e: Exception) { - mActivity.showErrorToast("Setup camera outputs $e") - } - } - - private fun chooseOptimalPreviewSize( - choices: Array, - textureViewWidth: Int, - textureViewHeight: Int, - maxWidth: Int, - maxHeight: Int, - selectedResolution: MySize - ): Size { - val bigEnough = ArrayList() - val notBigEnough = ArrayList() - val bigEnoughIncorrectAR = ArrayList() - val notBigEnoughIncorrectAR = ArrayList() - val width = selectedResolution.width - val height = selectedResolution.height - for (option in choices) { - if (option.width <= maxWidth && option.height <= maxHeight) { - if (option.height == option.width * height / width) { - if (option.width >= textureViewWidth && option.height >= textureViewHeight) { - bigEnough.add(option) - } else { - notBigEnough.add(option) - } - } else { - if (option.width >= textureViewWidth && option.height >= textureViewHeight) { - bigEnoughIncorrectAR.add(option) - } else { - notBigEnoughIncorrectAR.add(option) - } - } - } - } - - return when { - bigEnough.isNotEmpty() -> bigEnough.minByOrNull { it.width * it.height }!! - notBigEnough.isNotEmpty() -> notBigEnough.maxByOrNull { it.width * it.height }!! - bigEnoughIncorrectAR.isNotEmpty() -> bigEnoughIncorrectAR.minByOrNull { it.width * it.height }!! - notBigEnoughIncorrectAR.isNotEmpty() -> notBigEnoughIncorrectAR.maxByOrNull { it.width * it.height }!! - else -> selectedResolution.toSize() - } - } - - private fun getRealDisplaySize(): MySize { - val metrics = DisplayMetrics() - mActivity.windowManager.defaultDisplay.getRealMetrics(metrics) - return MySize(metrics.widthPixels, metrics.heightPixels) - } - - private val cameraStateCallback = object : CameraDevice.StateCallback() { - override fun onOpened(cameraDevice: CameraDevice) { - mCameraOpenCloseLock.release() - mCameraDevice = cameraDevice - createCameraPreviewSession() - mActivity.setCameraAvailable(true) - } - - override fun onDisconnected(cameraDevice: CameraDevice) { - mCameraOpenCloseLock.release() - cameraDevice.close() - mCameraDevice = null - mActivity.setCameraAvailable(false) - } - - override fun onError(cameraDevice: CameraDevice, error: Int) { - mCameraOpenCloseLock.release() - cameraDevice.close() - mCameraDevice = null - mActivity.setCameraAvailable(false) - } - } - - private fun createCameraPreviewSession() { - val stateCallback = object : CameraCaptureSession.StateCallback() { - override fun onConfigured(cameraCaptureSession: CameraCaptureSession) { - if (mCameraDevice == null) { - return - } - - mCaptureSession = cameraCaptureSession - try { - mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, getFrameRange()) - if (mIsInVideoMode) { - mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO) - mCaptureSession!!.setRepeatingRequest(mPreviewRequestBuilder!!.build(), null, mBackgroundHandler) - } else { - mPreviewRequestBuilder!!.apply { - set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) - setFlashAndExposure(this) - mPreviewRequest = build() - } - mCaptureSession!!.setRepeatingRequest(mPreviewRequest!!, mCaptureCallback, mBackgroundHandler) - } - mCameraState = STATE_PREVIEW - } catch (e: Exception) { - } - } - - override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {} - } - - try { - closeCaptureSession() - val texture = mTextureView.surfaceTexture!! - texture.setDefaultBufferSize(mPreviewSize!!.width, mPreviewSize!!.height) - - val surface = Surface(texture) - mPreviewRequestBuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) - mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW) - mPreviewRequestBuilder!!.addTarget(surface) - - if (mIsInVideoMode) { - mCameraDevice!!.createCaptureSession(Arrays.asList(surface), stateCallback, mBackgroundHandler) - } else { - mCameraDevice!!.createCaptureSession(Arrays.asList(surface, mImageReader!!.surface), stateCallback, null) - } - } catch (e: Exception) { - } - } - - private fun getFrameRange(): Range { - val ranges = mCameraCharacteristics?.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES) ?: return Range(0, 1) - var currRangeSize = -1 - var currMinRange = 0 - var result: Range? = null - for (range in ranges) { - val diff = range.upper - range.lower - if (diff > currRangeSize || (diff == currRangeSize && range.lower > currMinRange)) { - currRangeSize = diff - currMinRange = range.lower - result = range - } - } - - return result!! - } - - private fun updatePreview() { - try { - mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO) - mCaptureSession!!.setRepeatingRequest(mPreviewRequestBuilder!!.build(), null, mBackgroundHandler) - } catch (e: CameraAccessException) { - } - } - - private val mCaptureCallback = object : CameraCaptureSession.CaptureCallback() { - private fun process(result: CaptureResult) { - when (mCameraState) { - STATE_WAITING_LOCK -> { - val autoFocusState = result.get(CaptureResult.CONTROL_AF_STATE) - if (autoFocusState == null) { - captureStillPicture() - } else if (autoFocusState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || autoFocusState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { - val aeState = result.get(CaptureResult.CONTROL_AE_STATE) - if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { - captureStillPicture() - } else { - runPrecaptureSequence() - } - } - } - STATE_WAITING_PRECAPTURE -> { - val aeState = result.get(CaptureResult.CONTROL_AE_STATE) - if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { - mCameraState = STATE_WAITING_NON_PRECAPTURE - } - } - STATE_WAITING_NON_PRECAPTURE -> { - val aeState = result.get(CaptureResult.CONTROL_AE_STATE) - if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { - captureStillPicture() - } - } - } - } - - override fun onCaptureProgressed(session: CameraCaptureSession, request: CaptureRequest, partialResult: CaptureResult) { - process(partialResult) - } - - override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { - process(result) - } - } - - private fun runPrecaptureSequence() { - try { - mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START) - mCameraState = STATE_WAITING_PRECAPTURE - mCaptureSession!!.capture(mPreviewRequestBuilder!!.build(), mCaptureCallback, mBackgroundHandler) - } catch (e: CameraAccessException) { - } - } - - private fun captureStillPicture() { - try { - if (mCameraDevice == null) { - return - } - - if (mActivity.config.isSoundEnabled) { - mediaSoundHelper.playShutterSound() - } - - mCameraState = STATE_PICTURE_TAKEN - mRotationAtCapture = mActivity.mLastHandledOrientation - val jpegOrientation = (mSensorOrientation + compensateDeviceRotation(mRotationAtCapture)) % 360 - val captureBuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply { - if (mImageReader!!.surface == null) { - return - } - - addTarget(mImageReader!!.surface) - setFlashAndExposure(this) - set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) - set(CaptureRequest.JPEG_ORIENTATION, jpegOrientation) - set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE) - set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, getFrameRange()) - if (mZoomRect != null) { - set(CaptureRequest.SCALER_CROP_REGION, mZoomRect) - } - } - - val captureCallback = object : CameraCaptureSession.CaptureCallback() { - override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { - unlockFocus() - mActivity.toggleBottomButtons(false) - } - - override fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failure: CaptureFailure) { - super.onCaptureFailed(session, request, failure) - mActivity.toggleBottomButtons(false) - } - } - - mCaptureSession?.apply { - stopRepeating() - abortCaptures() - capture(captureBuilder.build(), captureCallback, null) - } - } catch (e: Exception) { - mActivity.showErrorToast("Capture picture $e") - } - } - - // inspired by https://gist.github.com/royshil/8c760c2485257c85a11cafd958548482 - private fun focusArea(x: Float, y: Float, drawCircle: Boolean) { - mLastFocusX = x - mLastFocusY = y - if (drawCircle) { - mActivity.drawFocusCircle(x, y) - } - - val captureCallbackHandler = object : CameraCaptureSession.CaptureCallback() { - override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { - super.onCaptureCompleted(session, request, result) - - if (request.tag == FOCUS_TAG) { - mCaptureSession?.setRepeatingRequest(mPreviewRequestBuilder!!.build(), mCaptureCallback, mBackgroundHandler) - } - } - } - - try { - mCaptureSession!!.stopRepeating() - - mPreviewRequestBuilder!!.apply { - set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE) - mCaptureSession!!.capture(build(), mCaptureCallback, mBackgroundHandler) - - // touch-to-focus inspired by OpenCamera - if (mCameraCharacteristics?.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)!! >= 1) { - val focusArea = getFocusArea(x, y) - val sensorRect = mCameraCharacteristics!!.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)!! - val meteringRect = convertAreaToMeteringRectangle(sensorRect, focusArea) - set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(meteringRect)) - } - - set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO) - set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO) - set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START) - setTag(FOCUS_TAG) - mCaptureSession!!.capture(build(), captureCallbackHandler, mBackgroundHandler) - set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE) - } - } catch (ignored: Exception) { - } - } - - private fun convertAreaToMeteringRectangle(sensorRect: Rect, focusArea: FocusArea): MeteringRectangle { - val camera2Rect = convertRectToCamera2(sensorRect, focusArea.rect) - return MeteringRectangle(camera2Rect, focusArea.weight) - } - - private fun convertRectToCamera2(cropRect: Rect, rect: Rect): Rect { - val leftF = (rect.left + 1000) / 2000f - val topF = (rect.top + 1000) / 2000f - val rightF = (rect.right + 1000) / 2000f - val bottomF = (rect.bottom + 1000) / 2000f - var left = (cropRect.left + leftF * (cropRect.width() - 1)).toInt() - var right = (cropRect.left + rightF * (cropRect.width() - 1)).toInt() - var top = (cropRect.top + topF * (cropRect.height() - 1)).toInt() - var bottom = (cropRect.top + bottomF * (cropRect.height() - 1)).toInt() - left = Math.max(left, cropRect.left) - right = Math.max(right, cropRect.left) - top = Math.max(top, cropRect.top) - bottom = Math.max(bottom, cropRect.top) - left = Math.min(left, cropRect.right) - right = Math.min(right, cropRect.right) - top = Math.min(top, cropRect.bottom) - bottom = Math.min(bottom, cropRect.bottom) - - return Rect(left, top, right, bottom) - } - - private fun getFocusArea(x: Float, y: Float): FocusArea { - val coords = floatArrayOf(x, y) - calculateCameraToPreviewMatrix() - mPreviewToCameraMatrix.mapPoints(coords) - val focusX = coords[0].toInt() - val focusY = coords[1].toInt() - - val focusSize = 50 - val rect = Rect() - rect.left = focusX - focusSize - rect.right = focusX + focusSize - rect.top = focusY - focusSize - rect.bottom = focusY + focusSize - - if (rect.left < -1000) { - rect.left = -1000 - rect.right = rect.left + 2 * focusSize - } else if (rect.right > 1000) { - rect.right = 1000 - rect.left = rect.right - 2 * focusSize - } - - if (rect.top < -1000) { - rect.top = -1000 - rect.bottom = rect.top + 2 * focusSize - } else if (rect.bottom > 1000) { - rect.bottom = 1000 - rect.top = rect.bottom - 2 * focusSize - } - - return FocusArea(rect, MeteringRectangle.METERING_WEIGHT_MAX) - } - - private fun calculateCameraToPreviewMatrix() { - val yScale = if (mUseFrontCamera) -1 else 1 - mCameraToPreviewMatrix.apply { - reset() - setScale(1f, yScale.toFloat()) - postRotate(mSensorOrientation.toFloat()) - postScale(mTextureView.width / 2000f, mTextureView.height / 2000f) - postTranslate(mTextureView.width / 2f, mTextureView.height / 2f) - invert(mPreviewToCameraMatrix) - } - } - - private fun lockFocus() { - try { - mPreviewRequestBuilder!!.apply { - set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START) - mCameraState = STATE_WAITING_LOCK - mCaptureSession?.capture(build(), mCaptureCallback, mBackgroundHandler) - } - } catch (e: CameraAccessException) { - mCameraState = STATE_PREVIEW - } - } - - private fun unlockFocus() { - try { - mPreviewRequestBuilder!!.apply { - set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE) - mCaptureSession?.capture(build(), mCaptureCallback, mBackgroundHandler) - } - mCameraState = STATE_PREVIEW - mCaptureSession?.setRepeatingRequest(mPreviewRequest!!, mCaptureCallback, mBackgroundHandler) - - if (mLastFocusX != 0f && mLastFocusY != 0f) { - focusArea(mLastFocusX, mLastFocusY, false) - } - } catch (e: Exception) { - } finally { - mCameraState = STATE_PREVIEW - } - } - - private fun setFlashAndExposure(builder: CaptureRequest.Builder) { - val aeMode = if (mFlashlightState == FLASH_AUTO) CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH else CameraMetadata.CONTROL_AE_MODE_ON - builder.apply { - set(CaptureRequest.FLASH_MODE, getFlashlightMode()) - set(CaptureRequest.CONTROL_AE_MODE, aeMode) - } - } - - private fun getCameraManager() = mActivity.getSystemService(Context.CAMERA_SERVICE) as CameraManager - - private fun getCameraCharacteristics(cameraId: String = mCameraId) = getCameraManager().getCameraCharacteristics(cameraId) - - private fun getFlashlightMode() = when (mFlashlightState) { - FLASH_ON -> CameraMetadata.FLASH_MODE_TORCH - else -> CameraMetadata.FLASH_MODE_OFF - } - - private fun setupMediaRecorder() { - try { - val videoSize = getCurrentResolution() - mLastVideoPath = mActivity.getOutputMediaFile(false) - val uri = if (context.isPathOnSD(mLastVideoPath)) { - val parentDocumentFile = context.getDocumentFile(mLastVideoPath.getParentPath()) - val documentFile = parentDocumentFile?.createFile("video/mp4", mLastVideoPath.getFilenameFromPath()) - ?: mActivity.getDocumentFile(mLastVideoPath) - documentFile!!.uri - } else { - Uri.fromFile(File(mLastVideoPath)) - } - - val fileDescriptor = context.contentResolver.openFileDescriptor(uri, "w")!!.fileDescriptor - - val rotation = mActivity.windowManager.defaultDisplay.rotation - mMediaRecorder!!.apply { - setAudioSource(MediaRecorder.AudioSource.MIC) - setVideoSource(MediaRecorder.VideoSource.SURFACE) - setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) - setOutputFile(fileDescriptor) - setVideoEncodingBitRate(10000000) - setVideoFrameRate(30) - setVideoSize(videoSize.width, videoSize.height) - setVideoEncoder(MediaRecorder.VideoEncoder.H264) - setAudioEncoder(MediaRecorder.AudioEncoder.AAC) - when (mSensorOrientation) { - 90 -> setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation)) - 270 -> setOrientationHint(INVERSE_ORIENTATIONS.get(rotation)) - } - prepare() - } - } catch (e: Exception) { - mActivity.showErrorToast(e) - } - } - - private fun startRecording() { - mCameraState = STATE_STARTING_RECORDING - closeCaptureSession() - setupMediaRecorder() - if (mActivity.config.isSoundEnabled) { - mediaSoundHelper.playStartVideoRecordingSound() - } - - try { - val texture = mTextureView.surfaceTexture!! - texture.setDefaultBufferSize(mPreviewSize!!.width, mPreviewSize!!.height) - mPreviewRequestBuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply { - set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD) - set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, getFrameRange()) - } - - val surfaces = ArrayList() - val previewSurface = Surface(texture) - surfaces.add(previewSurface) - mPreviewRequestBuilder!!.addTarget(previewSurface) - - val recorderSurface = mMediaRecorder!!.surface - surfaces.add(recorderSurface) - mPreviewRequestBuilder!!.addTarget(recorderSurface) - mCameraDevice!!.createCaptureSession(surfaces, getCameraCaptureCallback(), mBackgroundHandler) - } catch (e: Exception) { - mActivity.showErrorToast(e) - mCameraState = STATE_PREVIEW - } - } - - private fun stopRecording() { - mCameraState = STATE_STOPING_RECORDING - if (mActivity.config.isSoundEnabled) { - mediaSoundHelper.playStopVideoRecordingSound() - } - - mIsRecording = false - try { - mMediaRecorder!!.stop() - mMediaRecorder!!.reset() - mActivity.rescanPaths(arrayListOf(mLastVideoPath)) { - mActivity.videoSaved(Uri.fromFile(File(mLastVideoPath))) - } - } catch (e: Exception) { - mActivity.toast(R.string.video_recording_failed, Toast.LENGTH_LONG) - openResolutionsDialog(true) - val fileDirItem = FileDirItem(mLastVideoPath, mLastVideoPath.getFilenameFromPath()) - mActivity.deleteFile(fileDirItem, false) - } finally { - Thread { - closeCamera() - openCamera(mTextureView.width, mTextureView.height) - }.start() - mActivity.setRecordingState(false) - } - } - - private fun getCameraCaptureCallback(): CameraCaptureSession.StateCallback { - val callback = object : CameraCaptureSession.StateCallback() { - override fun onConfigured(session: CameraCaptureSession) { - mCaptureSession = session - updatePreview() - mIsRecording = true - mActivity.runOnUiThread { - try { - mMediaRecorder?.start() - } catch (e: Exception) { - mActivity.showErrorToast(e) - mCameraState = STATE_PREVIEW - } - } - mActivity.setRecordingState(true) - mCameraState = STATE_RECORDING - } - - override fun onConfigureFailed(session: CameraCaptureSession) { - mCameraState = STATE_PREVIEW - } - } - - return callback - } - - private fun getAvailableVideoSizes(configMap: StreamConfigurationMap) = configMap.getOutputSizes(MediaRecorder::class.java).filter { - it.width <= MAX_VIDEO_WIDTH && it.height <= MAX_VIDEO_HEIGHT - } - - override fun setTargetUri(uri: Uri) { - mTargetUri = uri - } - - override fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean) { - mIsImageCaptureIntent = isImageCaptureIntent - } - - override fun setFlashlightState(state: Int) { - mFlashlightState = state - checkFlashlight() - } - - override fun getCameraState() = mCameraState - - override fun showChangeResolutionDialog() { - openResolutionsDialog(false) - } - - private fun openResolutionsDialog(openVideoResolutions: Boolean) { - val oldResolution = getCurrentResolution() - val configMap = mCameraCharacteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: return - val photoResolutions = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) } as ArrayList - val videoResolutions = getAvailableVideoSizes(configMap).map { MySize(it.width, it.height) } as ArrayList - ChangeResolutionDialog(mActivity, mUseFrontCamera, photoResolutions, videoResolutions, openVideoResolutions) { - if (oldResolution != getCurrentResolution()) { - if (mIsRecording) { - stopRecording() - } - closeCamera() - openCamera(mTextureView.width, mTextureView.height) - } - } - } - - override fun toggleFrontBackCamera() { - mUseFrontCamera = !mUseFrontCamera - closeCamera() - openCamera(mTextureView.width, mTextureView.height) - } - - override fun toggleFlashlight() { - val newState = ++mFlashlightState % if (mIsInVideoMode) 2 else 3 - setFlashlightState(newState) - } - - override fun tryTakePicture() { - if (mCameraState != STATE_PREVIEW) { - return - } - - if (mIsFocusSupported) { - lockFocus() - } else { - captureStillPicture() - } - } - - override fun toggleRecording() { - if (mCameraDevice == null || !mTextureView.isAvailable || mPreviewSize == null) { - return - } - - if (mCameraState != STATE_PREVIEW && mCameraState != STATE_RECORDING) { - return - } - - if (mIsRecording) { - stopRecording() - } else { - startRecording() - } - } - - override fun initPhotoMode() { - mIsInVideoMode = false - closeCamera() - openCamera(mTextureView.width, mTextureView.height) - } - - override fun initVideoMode() { - mLastFocusX = 0f - mLastFocusY = 0f - mIsInVideoMode = true - closeCamera() - openCamera(mTextureView.width, mTextureView.height) - } - - override fun checkFlashlight() { - if ((mCameraState == STATE_PREVIEW || mCameraState == STATE_RECORDING) && mIsFlashSupported) { - try { - setFlashAndExposure(mPreviewRequestBuilder!!) - mPreviewRequest = mPreviewRequestBuilder!!.build() - mCaptureSession?.setRepeatingRequest(mPreviewRequest!!, mCaptureCallback, mBackgroundHandler) - mActivity.updateFlashlightState(mFlashlightState) - } catch (e: Exception) { - } - } - } - - override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {} -} diff --git a/app/src/main/res/layout/dialog_change_resolution.xml b/app/src/main/res/layout/dialog_change_resolution.xml deleted file mode 100644 index c1ce661b..00000000 --- a/app/src/main/res/layout/dialog_change_resolution.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - -