Add front camera preview and camera switch UI
This commit is contained in:
parent
5be04b0217
commit
9deeb40217
5 changed files with 594 additions and 21 deletions
|
|
@ -57,6 +57,7 @@ class MainActivity : ComponentActivity() {
|
|||
@Inject lateinit var authSessionStore: AuthSessionStore
|
||||
|
||||
@Volatile private var isAuthenticated = false
|
||||
private var frontPreviewOpened = false
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
|
|
@ -97,7 +98,16 @@ class MainActivity : ComponentActivity() {
|
|||
})
|
||||
|
||||
lifecycleScope.launch {
|
||||
authSessionStore.token.collect { token -> isAuthenticated = !token.isNullOrBlank() }
|
||||
authSessionStore.token.collect { token ->
|
||||
isAuthenticated = !token.isNullOrBlank()
|
||||
|
||||
if (isAuthenticated && !frontPreviewOpened) {
|
||||
frontPreviewOpened = true
|
||||
runOnUiThread {
|
||||
openFrontPreview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerHardwareReceiver()
|
||||
|
|
@ -222,6 +232,20 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
private fun openFrontPreview() {
|
||||
if (!isAuthenticated) return
|
||||
|
||||
startActivity(
|
||||
android.content.Intent(
|
||||
this,
|
||||
com.bodycamera.twentyfoulabs.camera.M530CameraActivity::class.java
|
||||
).putExtra(
|
||||
com.bodycamera.twentyfoulabs.camera.M530CameraActivity.EXTRA_MODE,
|
||||
com.bodycamera.twentyfoulabs.camera.M530CameraActivity.MODE_FRONT_PREVIEW
|
||||
).addFlags(android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
)
|
||||
}
|
||||
private fun handlePhotoButton() {
|
||||
startActivity(
|
||||
android.content.Intent(
|
||||
|
|
@ -374,3 +398,4 @@ fun AppNavigation(viewModel: MainViewModel, onOpenPhotoCamera: () -> Unit, onOpe
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.bodycamera.twentyfoulabs.camera
|
||||
package com.bodycamera.twentyfoulabs.camera
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentValues
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
|
|
@ -54,6 +55,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
const val EXTRA_MODE = "mode"
|
||||
const val MODE_PHOTO = "photo"
|
||||
const val MODE_VIDEO = "video"
|
||||
const val MODE_FRONT_PREVIEW = "front_preview"
|
||||
}
|
||||
|
||||
private lateinit var previewView: PreviewView
|
||||
|
|
@ -63,6 +65,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
private lateinit var physicalHintText: TextView
|
||||
private lateinit var captureButton: Button
|
||||
private lateinit var closeButton: Button
|
||||
private lateinit var switchCameraButton: Button
|
||||
private lateinit var recordDot: TextView
|
||||
private lateinit var chronometer: Chronometer
|
||||
|
||||
|
|
@ -73,10 +76,11 @@ class M530CameraActivity : ComponentActivity() {
|
|||
private var cameraProvider: ProcessCameraProvider? = null
|
||||
private var lastPhysicalActionMs = 0L
|
||||
private var lastTouchActionMs = 0L
|
||||
private var frontPreviewUsesFrontCamera = true
|
||||
|
||||
private val cameraPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
if (granted) startCamera() else stateText.text = "Permiso de cámara requerido"
|
||||
if (granted) startCamera() else stateText.text = "Permiso de camara requerido"
|
||||
}
|
||||
|
||||
private val audioPermissionLauncher =
|
||||
|
|
@ -140,7 +144,11 @@ class M530CameraActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
modeText = TextView(this).apply {
|
||||
text = if (mode == MODE_VIDEO) "VIDEO" else "FOTO"
|
||||
text = when (mode) {
|
||||
MODE_VIDEO -> "VIDEO"
|
||||
MODE_FRONT_PREVIEW -> "FRONTAL"
|
||||
else -> "FOTO"
|
||||
}
|
||||
setTextColor(if (mode == MODE_VIDEO) 0xFFFF3B30.toInt() else 0xFFFFC400.toInt())
|
||||
textSize = 14f
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
|
|
@ -173,10 +181,11 @@ class M530CameraActivity : ComponentActivity() {
|
|||
setPadding(dp(10), dp(5), dp(10), dp(5))
|
||||
setBackgroundColor(0x7A000000)
|
||||
isClickable = false
|
||||
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
recordDot = TextView(this).apply {
|
||||
text = "●"
|
||||
text = "*"
|
||||
setTextColor(0xFFFF3B30.toInt())
|
||||
textSize = 18f
|
||||
visibility = View.GONE
|
||||
|
|
@ -203,33 +212,89 @@ class M530CameraActivity : ComponentActivity() {
|
|||
)
|
||||
|
||||
closeButton = Button(this).apply {
|
||||
text = "×"
|
||||
text = "X"
|
||||
gravity = Gravity.CENTER
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 25f
|
||||
textSize = 18f
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
minWidth = 0
|
||||
minHeight = 0
|
||||
setPadding(0, 0, 0, 0)
|
||||
background = circleDrawable(0xB0000000.toInt(), Color.WHITE, dp(1))
|
||||
|
||||
background = GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(0x99000000.toInt())
|
||||
setStroke(dp(1), 0xCCFFFFFF.toInt())
|
||||
}
|
||||
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
elevation = dp(12).toFloat()
|
||||
elevation = dp(8).toFloat()
|
||||
contentDescription = "Cerrar"
|
||||
|
||||
setOnClickListener {
|
||||
stopRecordingIfNeeded()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
root.addView(
|
||||
closeButton,
|
||||
FrameLayout.LayoutParams(dp(54), dp(54), Gravity.TOP or Gravity.END).apply {
|
||||
topMargin = dp(62)
|
||||
FrameLayout.LayoutParams(dp(44), dp(44), Gravity.TOP or Gravity.END).apply {
|
||||
topMargin = dp(60)
|
||||
rightMargin = dp(12)
|
||||
}
|
||||
)
|
||||
|
||||
switchCameraButton = Button(this).apply {
|
||||
text = "CAMARA POSTERIOR"
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 11f
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
gravity = Gravity.CENTER
|
||||
minWidth = 0
|
||||
minHeight = 0
|
||||
setPadding(dp(16), 0, dp(16), 0)
|
||||
|
||||
background = GradientDrawable().apply {
|
||||
shape = GradientDrawable.RECTANGLE
|
||||
cornerRadius = dp(24).toFloat()
|
||||
setColor(0xB31A1A1A.toInt())
|
||||
setStroke(dp(1), 0xCCFFFFFF.toInt())
|
||||
}
|
||||
|
||||
visibility = if (mode == MODE_FRONT_PREVIEW) View.VISIBLE else View.GONE
|
||||
elevation = dp(10).toFloat()
|
||||
contentDescription = "Cambiar camara"
|
||||
|
||||
setOnClickListener {
|
||||
if (mode == MODE_FRONT_PREVIEW) {
|
||||
frontPreviewUsesFrontCamera = !frontPreviewUsesFrontCamera
|
||||
|
||||
text = if (frontPreviewUsesFrontCamera)
|
||||
"CAMARA POSTERIOR"
|
||||
else
|
||||
"CAMARA FRONTAL"
|
||||
|
||||
startCamera()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.addView(
|
||||
switchCameraButton,
|
||||
FrameLayout.LayoutParams(
|
||||
dp(170),
|
||||
dp(46),
|
||||
Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
|
||||
).apply {
|
||||
bottomMargin = dp(76)
|
||||
}
|
||||
)
|
||||
|
||||
captureButton = Button(this).apply {
|
||||
text = if (mode == MODE_VIDEO) "REC" else "FOTO"
|
||||
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 15f
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
|
|
@ -289,14 +354,22 @@ class M530CameraActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
physicalHintText = TextView(this).apply {
|
||||
text = if (mode == MODE_VIDEO) "F5 VIDEO" else "F2 FOTO"
|
||||
text = when (mode) {
|
||||
MODE_VIDEO -> "F5 VIDEO"
|
||||
MODE_FRONT_PREVIEW -> "EN VIVO"
|
||||
else -> "F2 FOTO"
|
||||
}
|
||||
setTextColor(if (mode == MODE_VIDEO) 0xFFFF3B30.toInt() else 0xFFFFC400.toInt())
|
||||
textSize = 15f
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
}
|
||||
|
||||
stateText = TextView(this).apply {
|
||||
text = if (mode == MODE_VIDEO) "Preparando video..." else "Preparando cámara..."
|
||||
text = when (mode) {
|
||||
MODE_VIDEO -> "Preparando video..."
|
||||
MODE_FRONT_PREVIEW -> "Preparando camara frontal..."
|
||||
else -> "Preparando camara..."
|
||||
}
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 13f
|
||||
gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||
|
|
@ -335,10 +408,10 @@ class M530CameraActivity : ComponentActivity() {
|
|||
if (now - lastTouchActionMs < 350) return
|
||||
lastTouchActionMs = now
|
||||
|
||||
if (mode == MODE_VIDEO) {
|
||||
toggleVideoRecording()
|
||||
} else {
|
||||
takePhoto()
|
||||
when (mode) {
|
||||
MODE_VIDEO -> toggleVideoRecording()
|
||||
MODE_FRONT_PREVIEW -> Unit
|
||||
else -> takePhoto()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +428,25 @@ class M530CameraActivity : ComponentActivity() {
|
|||
|
||||
provider.unbindAll()
|
||||
|
||||
if (mode == MODE_VIDEO) {
|
||||
if (mode == MODE_FRONT_PREVIEW) {
|
||||
imageCapture = null
|
||||
videoCapture = null
|
||||
|
||||
provider.bindToLifecycle(
|
||||
this,
|
||||
if (frontPreviewUsesFrontCamera)
|
||||
CameraSelector.DEFAULT_FRONT_CAMERA
|
||||
else
|
||||
CameraSelector.DEFAULT_BACK_CAMERA,
|
||||
preview
|
||||
)
|
||||
|
||||
stateText.text = if (frontPreviewUsesFrontCamera)
|
||||
"Camara frontal lista"
|
||||
else
|
||||
"Camara posterior lista"
|
||||
captureButton.isEnabled = false
|
||||
} else if (mode == MODE_VIDEO) {
|
||||
val recorder = Recorder.Builder()
|
||||
.setQualitySelector(
|
||||
QualitySelector.from(
|
||||
|
|
@ -397,7 +488,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
captureButton.isEnabled = true
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
stateText.text = "Error de cámara"
|
||||
stateText.text = "Error de camara"
|
||||
captureButton.isEnabled = false
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(this))
|
||||
|
|
@ -405,7 +496,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
|
||||
private fun takePhoto() {
|
||||
val capture = imageCapture ?: run {
|
||||
stateText.text = "Cámara no lista"
|
||||
stateText.text = "Camara no lista"
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -624,7 +715,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
chronometer.visibility = View.GONE
|
||||
recordDot.visibility = View.GONE
|
||||
|
||||
stateText.text = if (error) "Error de grabación" else "Video guardado"
|
||||
stateText.text = if (error) "Error de grabacion" else "Video guardado"
|
||||
physicalHintText.text = "F5 VIDEO"
|
||||
|
||||
captureButton.text = "REC"
|
||||
|
|
@ -641,6 +732,7 @@ class M530CameraActivity : ComponentActivity() {
|
|||
setBackgroundColor(Color.WHITE)
|
||||
alpha = 0.75f
|
||||
isClickable = false
|
||||
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||
}
|
||||
val root = findViewById<FrameLayout>(android.R.id.content)
|
||||
root.addView(
|
||||
|
|
@ -712,3 +804,8 @@ class M530CameraActivity : ComponentActivity() {
|
|||
private fun timestamp(): String =
|
||||
SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US).format(Date())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue