add some initial PreviewCameraTwo implementation from the sample code

This commit is contained in:
tibbi 2018-05-28 21:47:23 +02:00
parent 77fb00097c
commit 26f33e8c26
5 changed files with 408 additions and 4 deletions

View File

@ -84,6 +84,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
if (hasStorageAndCameraPermissions()) { if (hasStorageAndCameraPermissions()) {
mOrientationEventListener.enable() mOrientationEventListener.enable()
} }
mPreview?.onResumed()
} }
override fun onPause() { override fun onPause() {
@ -102,6 +103,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
if (mPreview?.getCameraState() == STATE_PICTURE_TAKEN) { if (mPreview?.getCameraState() == STATE_PICTURE_TAKEN) {
toast(R.string.photo_not_saved) toast(R.string.photo_not_saved)
} }
mPreview?.onPaused()
} }
override fun onDestroy() { override fun onDestroy() {
@ -194,7 +196,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
(btn_holder.layoutParams as RelativeLayout.LayoutParams).setMargins(0, 0, 0, (navBarHeight + resources.getDimension(R.dimen.activity_margin)).toInt()) (btn_holder.layoutParams as RelativeLayout.LayoutParams).setMargins(0, 0, 0, (navBarHeight + resources.getDimension(R.dimen.activity_margin)).toInt())
mPreview = if (isLollipopPlus()) PreviewCameraTwo(this) else PreviewCameraOne(this, camera_surface_view, this) mPreview = if (isLollipopPlus()) PreviewCameraTwo(this, camera_texture_view) else PreviewCameraOne(this, camera_surface_view, this)
view_holder.addView(mPreview as ViewGroup) view_holder.addView(mPreview as ViewGroup)
val imageDrawable = if (config.lastUsedCamera == mCameraImpl.getBackCameraId()) R.drawable.ic_camera_front else R.drawable.ic_camera_rear val imageDrawable = if (config.lastUsedCamera == mCameraImpl.getBackCameraId()) R.drawable.ic_camera_front else R.drawable.ic_camera_rear

View File

@ -31,3 +31,6 @@ const val FLASH_AUTO = 2
// camera states // camera states
const val STATE_PREVIEW = 0 const val STATE_PREVIEW = 0
const val STATE_PICTURE_TAKEN = 1 const val STATE_PICTURE_TAKEN = 1
const val STATE_WAITING_LOCK = 2
const val STATE_WAITING_PRECAPTURE = 3
const val STATE_WAITING_NON_PRECAPTURE = 4

View File

@ -3,6 +3,10 @@ package com.simplemobiletools.camera.interfaces
import android.net.Uri import android.net.Uri
interface MyPreview { interface MyPreview {
fun onResumed()
fun onPaused()
fun setTargetUri(uri: Uri) fun setTargetUri(uri: Uri)
fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean) fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean)

View File

@ -113,6 +113,10 @@ class PreviewCameraOne : ViewGroup, SurfaceHolder.Callback, MyPreview {
} }
} }
override fun onResumed() {}
override fun onPaused() {}
override fun tryInitVideoMode() { override fun tryInitVideoMode() {
if (mIsSurfaceCreated) { if (mIsSurfaceCreated) {
initVideoMode() initVideoMode()

View File

@ -1,14 +1,404 @@
package com.simplemobiletools.camera.views package com.simplemobiletools.camera.views
import android.annotation.TargetApi
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.graphics.*
import android.hardware.camera2.*
import android.media.ImageReader
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.util.Size
import android.util.SparseIntArray
import android.view.Surface
import android.view.TextureView
import android.view.ViewGroup import android.view.ViewGroup
import com.simplemobiletools.camera.helpers.STATE_PREVIEW import com.simplemobiletools.camera.activities.MainActivity
import com.simplemobiletools.camera.helpers.*
import com.simplemobiletools.camera.interfaces.MyPreview import com.simplemobiletools.camera.interfaces.MyPreview
import java.util.*
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
class PreviewCameraTwo(context: Context) : ViewGroup(context), MyPreview { // based on the Android Camera2 sample at https://github.com/googlesamples/android-Camera2Basic
private var mTargetUri: Uri? = null @TargetApi(Build.VERSION_CODES.LOLLIPOP)
class PreviewCameraTwo : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
private val MAX_PREVIEW_WIDTH = 1920
private val MAX_PREVIEW_HEIGHT = 1080
private val ORIENTATIONS = SparseIntArray().apply {
append(Surface.ROTATION_0, 90)
append(Surface.ROTATION_90, 0)
append(Surface.ROTATION_180, 270)
append(Surface.ROTATION_270, 180)
}
private lateinit var mActivity: MainActivity
private lateinit var mTextureView: AutoFitTextureView
private var mSensorOrientation = 0
private var mIsFlashSupported = true
private var mIsImageCaptureIntent = false private var mIsImageCaptureIntent = false
private var mCameraId = ""
private var mCameraState = STATE_PREVIEW
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 mCaptureSession: CameraCaptureSession? = null
private var mPreviewRequestBuilder: CaptureRequest.Builder? = null
private var mPreviewRequest: CaptureRequest? = null
private val mCameraOpenCloseLock = Semaphore(1)
constructor(context: Context) : super(context)
constructor(activity: MainActivity, textureView: AutoFitTextureView) : super(activity) {
mActivity = activity
mTextureView = textureView
}
override fun onResumed() {
startBackgroundThread()
if (mTextureView.isAvailable) {
openCamera(mTextureView.width, mTextureView.height)
} else {
mTextureView.surfaceTextureListener = this
}
}
override fun onPaused() {
stopBackgroundThread()
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
configureTransform(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() {
mBackgroundThread?.quitSafely()
try {
mBackgroundThread?.join()
mBackgroundThread = null
mBackgroundHandler = null
} catch (e: InterruptedException) {
}
}
private fun openCamera(width: Int, height: Int) {
setUpCameraOutputs(width, height)
configureTransform(width, height)
val manager = mActivity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw RuntimeException("Time out waiting to lock camera opening.")
}
manager.openCamera(mCameraId, cameraStateCallback, mBackgroundHandler)
} catch (e: Exception) {
}
}
private val imageAvailableListener = ImageReader.OnImageAvailableListener { reader -> val image = reader.acquireNextImage() }
private fun setUpCameraOutputs(width: Int, height: Int) {
val manager = mActivity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
for (cameraId in manager.cameraIdList) {
val characteristics = manager.getCameraCharacteristics(cameraId)
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
val largest = map.getOutputSizes(ImageFormat.JPEG).maxBy { it.width * it.height }
mImageReader = ImageReader.newInstance(largest!!.width, largest.height, ImageFormat.JPEG, 2)
mImageReader!!.setOnImageAvailableListener(imageAvailableListener, mBackgroundHandler)
val displayRotation = mActivity.windowManager.defaultDisplay.rotation
mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
var swappedDimensions = false
when (displayRotation) {
Surface.ROTATION_0, Surface.ROTATION_180 -> if (mSensorOrientation == 90 || mSensorOrientation == 270) {
swappedDimensions = true
}
Surface.ROTATION_90, Surface.ROTATION_270 -> if (mSensorOrientation == 0 || mSensorOrientation == 180) {
swappedDimensions = true
}
}
val displaySize = Point()
mActivity.windowManager.defaultDisplay.getSize(displaySize)
var rotatedPreviewWidth = width
var rotatedPreviewHeight = height
var maxPreviewWidth = displaySize.x
var maxPreviewHeight = displaySize.y
if (swappedDimensions) {
rotatedPreviewWidth = height
rotatedPreviewHeight = width
maxPreviewWidth = displaySize.y
maxPreviewHeight = displaySize.x
}
if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
maxPreviewWidth = MAX_PREVIEW_WIDTH
}
if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
maxPreviewHeight = MAX_PREVIEW_HEIGHT
}
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest)
val orientation = resources.configuration.orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mTextureView.setAspectRatio(mPreviewSize!!.width, mPreviewSize!!.height)
} else {
mTextureView.setAspectRatio(mPreviewSize!!.height, mPreviewSize!!.width)
}
val available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
mIsFlashSupported = available ?: false
mCameraId = cameraId
return
}
} catch (e: Exception) {
}
}
private fun chooseOptimalSize(choices: Array<Size>, textureViewWidth: Int, textureViewHeight: Int, maxWidth: Int, maxHeight: Int, aspectRatio: Size): Size {
val bigEnough = ArrayList<Size>()
val notBigEnough = ArrayList<Size>()
val width = aspectRatio.width
val height = aspectRatio.height
for (option in choices) {
if (option.width <= maxWidth && option.height <= maxHeight && option.height == option.width * height / width) {
if (option.width >= textureViewWidth && option.height >= textureViewHeight) {
bigEnough.add(option)
} else {
notBigEnough.add(option)
}
}
}
return when {
bigEnough.size > 0 -> bigEnough.minBy { it.width * it.height }!!
notBigEnough.size > 0 -> notBigEnough.maxBy { it.width * it.height }!!
else -> choices[0]
}
}
private fun configureTransform(viewWidth: Int, viewHeight: Int) {
if (mPreviewSize == null) {
return
}
val rotation = mActivity.windowManager.defaultDisplay.rotation
val matrix = Matrix()
val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
val bufferRect = RectF(0f, 0f, mPreviewSize!!.height.toFloat(), mPreviewSize!!.width.toFloat())
val centerX = viewRect.centerX()
val centerY = viewRect.centerY()
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
val scale = Math.max(viewHeight.toFloat() / mPreviewSize!!.height, viewWidth.toFloat() / mPreviewSize!!.width)
matrix.postScale(scale, scale, centerX, centerY)
matrix.postRotate((90 * (rotation - 2)).toFloat(), centerX, centerY)
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180f, centerX, centerY)
}
mTextureView.setTransform(matrix)
}
private var cameraStateCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
mCameraOpenCloseLock.release()
mCameraDevice = cameraDevice
createCameraPreviewSession()
}
override fun onDisconnected(cameraDevice: CameraDevice) {
mCameraOpenCloseLock.release()
cameraDevice.close()
mCameraDevice = null
}
override fun onError(cameraDevice: CameraDevice, error: Int) {
mCameraOpenCloseLock.release()
cameraDevice.close()
mCameraDevice = null
}
}
private fun createCameraPreviewSession() {
try {
val texture = mTextureView.surfaceTexture!!
texture.setDefaultBufferSize(mPreviewSize!!.width, mPreviewSize!!.height)
val surface = Surface(texture)
mPreviewRequestBuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
mPreviewRequestBuilder!!.addTarget(surface)
mCameraDevice!!.createCaptureSession(Arrays.asList(surface, mImageReader!!.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
if (mCameraDevice == null) {
return
}
mCaptureSession = cameraCaptureSession
try {
mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
setAutoFlash(mPreviewRequestBuilder!!)
mPreviewRequest = mPreviewRequestBuilder!!.build()
mCaptureSession!!.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler)
} catch (e: CameraAccessException) {
}
}
override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {
}
}, null
)
} catch (e: CameraAccessException) {
}
}
private fun setAutoFlash(requestBuilder: CaptureRequest.Builder) {
if (mIsFlashSupported) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
}
}
private val mCaptureCallback = object : CameraCaptureSession.CaptureCallback() {
private fun process(result: CaptureResult) {
when (mCameraState) {
STATE_WAITING_LOCK -> {
val afState = result.get(CaptureResult.CONTROL_AF_STATE)
if (afState == null) {
captureStillPicture()
} else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mCameraState = STATE_PICTURE_TAKEN
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) {
mCameraState = STATE_PICTURE_TAKEN
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
}
val captureBuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
captureBuilder.addTarget(mImageReader!!.surface)
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
setAutoFlash(captureBuilder)
val rotation = mActivity.windowManager.defaultDisplay.rotation
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation))
val CaptureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
unlockFocus()
}
}
mCaptureSession!!.apply {
stopRepeating()
abortCaptures()
capture(captureBuilder.build(), CaptureCallback, null)
}
} catch (e: CameraAccessException) {
}
}
private fun getOrientation(rotation: Int) = (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360
private fun unlockFocus() {
try {
mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL)
setAutoFlash(mPreviewRequestBuilder!!)
mCaptureSession!!.capture(mPreviewRequestBuilder!!.build(), mCaptureCallback, mBackgroundHandler)
mCameraState = STATE_PREVIEW
mCaptureSession!!.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler)
} catch (e: CameraAccessException) {
}
}
private fun lockFocus() {
try {
mPreviewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)
mCameraState = STATE_WAITING_LOCK
mCaptureSession!!.capture(mPreviewRequestBuilder!!.build(), mCaptureCallback, mBackgroundHandler)
} catch (e: CameraAccessException) {
}
}
private fun takePicture() {
lockFocus()
}
override fun setTargetUri(uri: Uri) { override fun setTargetUri(uri: Uri) {
mTargetUri = uri mTargetUri = uri
@ -42,6 +432,7 @@ class PreviewCameraTwo(context: Context) : ViewGroup(context), MyPreview {
} }
override fun tryTakePicture() { override fun tryTakePicture() {
takePicture()
} }
override fun toggleRecording(): Boolean { override fun toggleRecording(): Boolean {