parent
94305b9125
commit
b0e88d08b1
@ -0,0 +1,188 @@ |
||||
package net.pokeranalytics.android.ui.activity.components |
||||
|
||||
import android.Manifest |
||||
import android.app.Activity |
||||
import android.content.ContentValues |
||||
import android.content.Intent |
||||
import android.content.pm.PackageManager |
||||
import android.os.Build |
||||
import android.os.Bundle |
||||
import android.provider.MediaStore |
||||
import android.widget.Toast |
||||
import androidx.camera.core.CameraSelector |
||||
import androidx.camera.core.ImageCapture |
||||
import androidx.camera.core.ImageCaptureException |
||||
import androidx.camera.core.Preview |
||||
import androidx.camera.lifecycle.ProcessCameraProvider |
||||
import androidx.core.app.ActivityCompat |
||||
import androidx.core.content.ContextCompat |
||||
import androidx.fragment.app.Fragment |
||||
import net.pokeranalytics.android.databinding.ActivityCameraBinding |
||||
import timber.log.Timber |
||||
import java.text.SimpleDateFormat |
||||
import java.util.* |
||||
import java.util.concurrent.ExecutorService |
||||
import java.util.concurrent.Executors |
||||
|
||||
class CameraActivity : BaseActivity() { |
||||
|
||||
companion object { |
||||
|
||||
const val IMAGE_URI = "image_uri" |
||||
|
||||
fun newInstanceForResult(fragment: Fragment, code: RequestCode) { |
||||
val intent = Intent(fragment.requireContext(), CameraActivity::class.java) |
||||
fragment.startActivityForResult(intent, code.value) |
||||
} |
||||
|
||||
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" |
||||
private const val REQUEST_CODE_PERMISSIONS = 10 |
||||
private val REQUIRED_PERMISSIONS = |
||||
mutableListOf ( |
||||
Manifest.permission.CAMERA, |
||||
Manifest.permission.RECORD_AUDIO |
||||
).apply { |
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { |
||||
add(Manifest.permission.WRITE_EXTERNAL_STORAGE) |
||||
} |
||||
}.toTypedArray() |
||||
|
||||
} |
||||
|
||||
private lateinit var viewBinding: ActivityCameraBinding |
||||
|
||||
private lateinit var cameraExecutor: ExecutorService |
||||
private var imageCapture: ImageCapture? = null |
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
|
||||
this.viewBinding = ActivityCameraBinding.inflate(layoutInflater) |
||||
setContentView(viewBinding.root) |
||||
|
||||
if (allPermissionsGranted()) { |
||||
cameraExecutor = Executors.newSingleThreadExecutor() |
||||
startCamera() |
||||
} else { |
||||
ActivityCompat.requestPermissions( |
||||
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS) |
||||
} |
||||
|
||||
viewBinding.imageCaptureButton.setOnClickListener { takePhoto() } |
||||
cameraExecutor = Executors.newSingleThreadExecutor() |
||||
|
||||
} |
||||
|
||||
override fun onDestroy() { |
||||
super.onDestroy() |
||||
this.cameraExecutor.shutdown() |
||||
} |
||||
|
||||
override fun onRequestPermissionsResult( |
||||
requestCode: Int, |
||||
permissions: Array<out String>, |
||||
grantResults: IntArray |
||||
) { |
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||
|
||||
if (requestCode == REQUEST_CODE_PERMISSIONS) { |
||||
if (allPermissionsGranted()) { |
||||
startCamera() |
||||
} else { |
||||
Toast.makeText(this, |
||||
"Permissions not granted by the user.", |
||||
Toast.LENGTH_SHORT).show() |
||||
finish() |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { |
||||
ContextCompat.checkSelfPermission( |
||||
baseContext, it) == PackageManager.PERMISSION_GRANTED |
||||
} |
||||
|
||||
private fun startCamera() { |
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(this) |
||||
|
||||
cameraProviderFuture.addListener({ |
||||
// Used to bind the lifecycle of cameras to the lifecycle owner |
||||
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() |
||||
|
||||
// Preview |
||||
val preview = Preview.Builder() |
||||
.build() |
||||
.also { |
||||
it.setSurfaceProvider(this.viewBinding.viewFinder.surfaceProvider) |
||||
} |
||||
|
||||
imageCapture = ImageCapture.Builder().build() |
||||
|
||||
// Select back camera as a default |
||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA |
||||
|
||||
try { |
||||
// Unbind use cases before rebinding |
||||
cameraProvider.unbindAll() |
||||
|
||||
// Bind use cases to camera |
||||
cameraProvider.bindToLifecycle( |
||||
this, cameraSelector, preview, imageCapture) |
||||
|
||||
} catch(exc: Exception) { |
||||
Timber.e(exc) |
||||
} |
||||
|
||||
}, ContextCompat.getMainExecutor(this)) |
||||
} |
||||
|
||||
|
||||
private fun takePhoto() { |
||||
// Get a stable reference of the modifiable image capture use case |
||||
val imageCapture = imageCapture ?: return |
||||
|
||||
// Create time stamped name and MediaStore entry. |
||||
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) |
||||
.format(System.currentTimeMillis()) |
||||
val contentValues = ContentValues().apply { |
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, name) |
||||
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") |
||||
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { |
||||
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image") |
||||
} |
||||
} |
||||
|
||||
// Create output options object which contains file + metadata |
||||
val outputOptions = ImageCapture.OutputFileOptions |
||||
.Builder(contentResolver, |
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, |
||||
contentValues) |
||||
.build() |
||||
|
||||
// Set up image capture listener, which is triggered after photo has |
||||
// been taken |
||||
imageCapture.takePicture( |
||||
outputOptions, |
||||
ContextCompat.getMainExecutor(this), |
||||
object : ImageCapture.OnImageSavedCallback { |
||||
override fun onError(e: ImageCaptureException) { |
||||
Timber.e(e) |
||||
} |
||||
|
||||
override fun onImageSaved(output: ImageCapture.OutputFileResults) { |
||||
|
||||
val msg = "Photo capture succeeded: ${output.savedUri}" |
||||
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() |
||||
Timber.d(msg) |
||||
|
||||
val intent = Intent() |
||||
intent.putExtra(IMAGE_URI, output.savedUri.toString()) |
||||
setResult(Activity.RESULT_OK, intent) |
||||
|
||||
finish() |
||||
} |
||||
} |
||||
) |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,31 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<androidx.constraintlayout.widget.ConstraintLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<androidx.camera.view.PreviewView |
||||
android:id="@+id/viewFinder" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
|
||||
<Button |
||||
android:id="@+id/image_capture_button" |
||||
android:layout_width="110dp" |
||||
android:layout_height="110dp" |
||||
android:layout_marginBottom="50dp" |
||||
android:layout_marginEnd="50dp" |
||||
android:elevation="2dp" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
app:layout_constraintLeft_toLeftOf="parent" |
||||
app:layout_constraintEnd_toStartOf="@id/vertical_centerline" /> |
||||
|
||||
<androidx.constraintlayout.widget.Guideline |
||||
android:id="@+id/vertical_centerline" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical" |
||||
app:layout_constraintGuide_percent=".50" /> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
Loading…
Reference in new issue