diff --git a/app/build.gradle b/app/build.gradle index a11eda862f..a351c2c6ed 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1590 - versionName "0.15.90" + versionCode 1591 + versionName "0.15.91" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -131,6 +131,11 @@ dependencies { implementation 'com.mikepenz:crossfadedrawerlayout:1.1.0@aar' implementation('com.mikepenz:materialdrawer:6.1.1@aar') { transitive = true} + implementation "androidx.camera:camera-core:1.1.0" + implementation "androidx.camera:camera-camera2:1.1.0" + implementation "androidx.camera:camera-lifecycle:1.1.0" + implementation "androidx.camera:camera-view:1.0.0-alpha31" + def dagger_hilt_version = "2.51.1" implementation "com.google.dagger:hilt-android:$dagger_hilt_version" kapt "com.google.dagger:hilt-android-compiler:$dagger_hilt_version" diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt index 97c7f0ed25..fd61014b61 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt @@ -163,7 +163,12 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { b.putInt("stepNum", stepNumber) takeExam.arguments = b homeItemClickListener?.openCallFragment(takeExam) - CapturePhoto(this) + context?.let { it1 -> + CapturePhoto(it1, object : ImageCaptureCallback { + override fun onImageCapture(fileUri: String?) { + } + }) + } } } val downloadedResources: List = cRealm.where(RealmMyLibrary::class.java).equalTo("stepId", stepId).equalTo("resourceOffline", true).isNotNull("resourceLocalAddress").findAll() diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt index 652ca3a441..b698a9102f 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt @@ -209,7 +209,12 @@ class TakeExamFragment : BaseExamFragment(), View.OnClickListener, CompoundButto private fun capturePhoto() { try { if (isCertified && !isMySurvey) { - CapturePhoto(this) + context?.let { it1 -> + CapturePhoto(it1, object : ImageCaptureCallback { + override fun onImageCapture(fileUri: String?) { + } + }) + } } } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt b/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt index 26e957b477..59efc46ee8 100644 --- a/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt +++ b/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt @@ -1,34 +1,62 @@ package org.ole.planet.myplanet.utilities - +import android.Manifest +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.ImageFormat import android.graphics.SurfaceTexture -import android.hardware.Camera -import android.hardware.Camera.CameraInfo +import android.hardware.camera2.* +import android.media.ImageReader +import android.os.Handler +import android.os.HandlerThread +import android.util.Size +import android.view.Surface +import androidx.core.content.ContextCompat import java.io.File import java.io.FileOutputStream -import java.util.Date +import java.util.* object CameraUtils { + private var cameraDevice: CameraDevice? = null + private var captureSession: CameraCaptureSession? = null + private var imageReader: ImageReader? = null + private var backgroundHandler: Handler + private var backgroundThread: HandlerThread @JvmStatic - fun CapturePhoto(callback: ImageCaptureCallback) { - val cameraInfo = CameraInfo() - val frontCamera = 1 - val camera: Camera = try { - Camera.getCameraInfo(frontCamera, cameraInfo) - Camera.open(frontCamera) - } catch (e: RuntimeException) { - e.printStackTrace() + fun CapturePhoto(context: Context, callback: ImageCaptureCallback) { + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.CAMERA + ) != PackageManager.PERMISSION_GRANTED + ) { return } - try { - camera.setPreviewTexture(SurfaceTexture(0)) - camera.startPreview() - camera.takePicture(null, null) { data, camera -> - savePicture(data, callback) - camera.release() + openCamera(context) + imageReader = ImageReader.newInstance( + IMAGE_WIDTH, IMAGE_HEIGHT, ImageFormat.JPEG, 1 + ) + imageReader?.setOnImageAvailableListener({ reader -> + val image = reader.acquireLatestImage() + val buffer = image.planes[0].buffer + val bytes = ByteArray(buffer.capacity()) + buffer.get(bytes) + savePicture(bytes, callback) + image.close() + }, backgroundHandler) + + val captureBuilder = cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE) + captureBuilder?.addTarget(imageReader!!.surface) + captureBuilder?.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) + + val captureCallback = object : CameraCaptureSession.CaptureCallback() { + override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { + super.onCaptureCompleted(session, request, result) } - } catch (e: Exception) { - camera.release() } + + captureSession?.stopRepeating() + captureSession?.abortCaptures() + captureSession?.capture(captureBuilder!!.build(), captureCallback, null) } @JvmStatic @@ -49,8 +77,90 @@ object CameraUtils { error.printStackTrace() } } + @SuppressLint("MissingPermission") + @JvmStatic + private fun openCamera(context: Context) { + val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + try { + val cameraId = manager.cameraIdList[0] // Assuming we want to use the first (rear) camera + val characteristics = manager.getCameraCharacteristics(cameraId) + val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + val largest = Collections.max( + listOf(*map!!.getOutputSizes(ImageFormat.JPEG)), + CompareSizesByArea() + ) + val reader = ImageReader.newInstance(largest.width, largest.height, ImageFormat.JPEG, 2) + manager.openCamera(cameraId, object : CameraDevice.StateCallback() { + override fun onOpened(camera: CameraDevice) { + cameraDevice = camera + createCameraPreview() + } + + override fun onDisconnected(camera: CameraDevice) { + cameraDevice?.close() + } + + override fun onError(camera: CameraDevice, error: Int) { + cameraDevice?.close() + cameraDevice = null + } + }, null) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + } + + private fun createCameraPreview() { + try { + val texture = SurfaceTexture(0) + texture.setDefaultBufferSize(IMAGE_WIDTH, IMAGE_HEIGHT) + val surface = Surface(texture) + val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) + captureRequestBuilder.addTarget(surface) + cameraDevice!!.createCaptureSession( + listOf(surface), + object : CameraCaptureSession.StateCallback() { + override fun onConfigured(session: CameraCaptureSession) { + if (cameraDevice == null) return + captureSession = session + try { + captureRequestBuilder.set( + CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE + ) + captureSession!!.setRepeatingRequest( + captureRequestBuilder.build(), + null, + backgroundHandler + ) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + } + + override fun onConfigureFailed(session: CameraCaptureSession) {} + }, + backgroundHandler + ) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + } interface ImageCaptureCallback { fun onImageCapture(fileUri: String?) } + + private class CompareSizesByArea : Comparator { + override fun compare(lhs: Size, rhs: Size): Int { + return java.lang.Long.signum(lhs.width.toLong() * lhs.height - rhs.width.toLong() * rhs.height) + } + } + init { + backgroundThread = HandlerThread("CameraBackground") + backgroundThread.start() + backgroundHandler = Handler(backgroundThread.looper) + } + private const val IMAGE_WIDTH = 640 + private const val IMAGE_HEIGHT = 480 } \ No newline at end of file