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
120
add_camera_switch.ps1
Normal file
120
add_camera_switch.ps1
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
$path = 'app\src\main\kotlin\com\bodycamera\twentyfoulabs\camera\M530CameraActivity.kt'
|
||||||
|
|
||||||
|
$lines = Get-Content $path
|
||||||
|
$result = New-Object System.Collections.Generic.List[string]
|
||||||
|
|
||||||
|
$addedVariable = $false
|
||||||
|
$addedButtonDeclaration = $false
|
||||||
|
$addedButtonUi = $false
|
||||||
|
$replacedSelector = $false
|
||||||
|
$replacedState = $false
|
||||||
|
|
||||||
|
foreach ($line in $lines) {
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 1. Variable para saber que camara usa el preview
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not $addedVariable -and
|
||||||
|
$line -match 'private var lastTouchActionMs = 0L') {
|
||||||
|
|
||||||
|
$result.Add($line)
|
||||||
|
$result.Add(' private var frontPreviewUsesFrontCamera = true')
|
||||||
|
$addedVariable = $true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 2. Declaracion del boton
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not $addedButtonDeclaration -and
|
||||||
|
$line -match 'private lateinit var closeButton: Button') {
|
||||||
|
|
||||||
|
$result.Add($line)
|
||||||
|
$result.Add(' private lateinit var switchCameraButton: Button')
|
||||||
|
$addedButtonDeclaration = $true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 3. Insertar boton antes de captureButton
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not $addedButtonUi -and
|
||||||
|
$line -match '^\s*captureButton = Button\(this\)\.apply') {
|
||||||
|
|
||||||
|
$result.Add(' switchCameraButton = Button(this).apply {')
|
||||||
|
$result.Add(' text = "CAMBIAR"')
|
||||||
|
$result.Add(' setTextColor(Color.WHITE)')
|
||||||
|
$result.Add(' textSize = 12f')
|
||||||
|
$result.Add(' setTypeface(typeface, Typeface.BOLD)')
|
||||||
|
$result.Add(' gravity = Gravity.CENTER')
|
||||||
|
$result.Add(' minWidth = 0')
|
||||||
|
$result.Add(' minHeight = 0')
|
||||||
|
$result.Add(' setPadding(dp(8), 0, dp(8), 0)')
|
||||||
|
$result.Add(' background = circleDrawable(0xB0000000.toInt(), Color.WHITE, dp(1))')
|
||||||
|
$result.Add(' visibility = if (mode == MODE_FRONT_PREVIEW) View.VISIBLE else View.GONE')
|
||||||
|
$result.Add(' elevation = dp(12).toFloat()')
|
||||||
|
$result.Add('')
|
||||||
|
$result.Add(' setOnClickListener {')
|
||||||
|
$result.Add(' if (mode == MODE_FRONT_PREVIEW) {')
|
||||||
|
$result.Add(' frontPreviewUsesFrontCamera = !frontPreviewUsesFrontCamera')
|
||||||
|
$result.Add(' startCamera()')
|
||||||
|
$result.Add(' }')
|
||||||
|
$result.Add(' }')
|
||||||
|
$result.Add(' }')
|
||||||
|
$result.Add('')
|
||||||
|
$result.Add(' root.addView(')
|
||||||
|
$result.Add(' switchCameraButton,')
|
||||||
|
$result.Add(' FrameLayout.LayoutParams(dp(110), dp(48), Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL).apply {')
|
||||||
|
$result.Add(' bottomMargin = dp(76)')
|
||||||
|
$result.Add(' }')
|
||||||
|
$result.Add(' )')
|
||||||
|
$result.Add('')
|
||||||
|
|
||||||
|
$result.Add($line)
|
||||||
|
$addedButtonUi = $true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 4. Selector dinamico frontal/posterior SOLO para preview
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not $replacedSelector -and
|
||||||
|
$line -match 'CameraSelector\.DEFAULT_FRONT_CAMERA') {
|
||||||
|
|
||||||
|
$result.Add(' if (frontPreviewUsesFrontCamera)')
|
||||||
|
$result.Add(' CameraSelector.DEFAULT_FRONT_CAMERA')
|
||||||
|
$result.Add(' else')
|
||||||
|
$result.Add(' CameraSelector.DEFAULT_BACK_CAMERA,')
|
||||||
|
$replacedSelector = $true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 5. Cambiar mensaje segun camara
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if (-not $replacedState -and
|
||||||
|
$line -match 'stateText\.text = "Camara frontal lista"') {
|
||||||
|
|
||||||
|
$result.Add(' stateText.text = if (frontPreviewUsesFrontCamera)')
|
||||||
|
$result.Add(' "Camara frontal lista"')
|
||||||
|
$result.Add(' else')
|
||||||
|
$result.Add(' "Camara posterior lista"')
|
||||||
|
$replacedState = $true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$result.Add($line)
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Content -Path $path -Value $result -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "CAMBIO DE CAMARA AGREGADO"
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "Variable: $addedVariable"
|
||||||
|
Write-Host "Declaracion boton: $addedButtonDeclaration"
|
||||||
|
Write-Host "UI boton: $addedButtonUi"
|
||||||
|
Write-Host "Selector: $replacedSelector"
|
||||||
|
Write-Host "Estado: $replacedState"
|
||||||
|
Write-Host ""
|
||||||
|
|
@ -57,6 +57,7 @@ class MainActivity : ComponentActivity() {
|
||||||
@Inject lateinit var authSessionStore: AuthSessionStore
|
@Inject lateinit var authSessionStore: AuthSessionStore
|
||||||
|
|
||||||
@Volatile private var isAuthenticated = false
|
@Volatile private var isAuthenticated = false
|
||||||
|
private var frontPreviewOpened = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MainActivity"
|
private const val TAG = "MainActivity"
|
||||||
|
|
@ -97,7 +98,16 @@ class MainActivity : ComponentActivity() {
|
||||||
})
|
})
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
authSessionStore.token.collect { token -> isAuthenticated = !token.isNullOrBlank() }
|
authSessionStore.token.collect { token ->
|
||||||
|
isAuthenticated = !token.isNullOrBlank()
|
||||||
|
|
||||||
|
if (isAuthenticated && !frontPreviewOpened) {
|
||||||
|
frontPreviewOpened = true
|
||||||
|
runOnUiThread {
|
||||||
|
openFrontPreview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHardwareReceiver()
|
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() {
|
private fun handlePhotoButton() {
|
||||||
startActivity(
|
startActivity(
|
||||||
android.content.Intent(
|
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.Manifest
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
|
|
@ -54,6 +55,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
const val EXTRA_MODE = "mode"
|
const val EXTRA_MODE = "mode"
|
||||||
const val MODE_PHOTO = "photo"
|
const val MODE_PHOTO = "photo"
|
||||||
const val MODE_VIDEO = "video"
|
const val MODE_VIDEO = "video"
|
||||||
|
const val MODE_FRONT_PREVIEW = "front_preview"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var previewView: PreviewView
|
private lateinit var previewView: PreviewView
|
||||||
|
|
@ -63,6 +65,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
private lateinit var physicalHintText: TextView
|
private lateinit var physicalHintText: TextView
|
||||||
private lateinit var captureButton: Button
|
private lateinit var captureButton: Button
|
||||||
private lateinit var closeButton: Button
|
private lateinit var closeButton: Button
|
||||||
|
private lateinit var switchCameraButton: Button
|
||||||
private lateinit var recordDot: TextView
|
private lateinit var recordDot: TextView
|
||||||
private lateinit var chronometer: Chronometer
|
private lateinit var chronometer: Chronometer
|
||||||
|
|
||||||
|
|
@ -73,10 +76,11 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
private var cameraProvider: ProcessCameraProvider? = null
|
private var cameraProvider: ProcessCameraProvider? = null
|
||||||
private var lastPhysicalActionMs = 0L
|
private var lastPhysicalActionMs = 0L
|
||||||
private var lastTouchActionMs = 0L
|
private var lastTouchActionMs = 0L
|
||||||
|
private var frontPreviewUsesFrontCamera = true
|
||||||
|
|
||||||
private val cameraPermissionLauncher =
|
private val cameraPermissionLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
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 =
|
private val audioPermissionLauncher =
|
||||||
|
|
@ -140,7 +144,11 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
modeText = TextView(this).apply {
|
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())
|
setTextColor(if (mode == MODE_VIDEO) 0xFFFF3B30.toInt() else 0xFFFFC400.toInt())
|
||||||
textSize = 14f
|
textSize = 14f
|
||||||
setTypeface(typeface, Typeface.BOLD)
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
|
@ -173,10 +181,11 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
setPadding(dp(10), dp(5), dp(10), dp(5))
|
setPadding(dp(10), dp(5), dp(10), dp(5))
|
||||||
setBackgroundColor(0x7A000000)
|
setBackgroundColor(0x7A000000)
|
||||||
isClickable = false
|
isClickable = false
|
||||||
|
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
recordDot = TextView(this).apply {
|
recordDot = TextView(this).apply {
|
||||||
text = "●"
|
text = "*"
|
||||||
setTextColor(0xFFFF3B30.toInt())
|
setTextColor(0xFFFF3B30.toInt())
|
||||||
textSize = 18f
|
textSize = 18f
|
||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
|
|
@ -203,33 +212,89 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
)
|
)
|
||||||
|
|
||||||
closeButton = Button(this).apply {
|
closeButton = Button(this).apply {
|
||||||
text = "×"
|
text = "X"
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
setTextColor(Color.WHITE)
|
setTextColor(Color.WHITE)
|
||||||
textSize = 25f
|
textSize = 18f
|
||||||
setTypeface(typeface, Typeface.BOLD)
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
minWidth = 0
|
minWidth = 0
|
||||||
minHeight = 0
|
minHeight = 0
|
||||||
setPadding(0, 0, 0, 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
|
isClickable = true
|
||||||
isFocusable = true
|
isFocusable = true
|
||||||
elevation = dp(12).toFloat()
|
elevation = dp(8).toFloat()
|
||||||
|
contentDescription = "Cerrar"
|
||||||
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
stopRecordingIfNeeded()
|
stopRecordingIfNeeded()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root.addView(
|
root.addView(
|
||||||
closeButton,
|
closeButton,
|
||||||
FrameLayout.LayoutParams(dp(54), dp(54), Gravity.TOP or Gravity.END).apply {
|
FrameLayout.LayoutParams(dp(44), dp(44), Gravity.TOP or Gravity.END).apply {
|
||||||
topMargin = dp(62)
|
topMargin = dp(60)
|
||||||
rightMargin = dp(12)
|
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 {
|
captureButton = Button(this).apply {
|
||||||
text = if (mode == MODE_VIDEO) "REC" else "FOTO"
|
text = if (mode == MODE_VIDEO) "REC" else "FOTO"
|
||||||
|
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||||
setTextColor(Color.WHITE)
|
setTextColor(Color.WHITE)
|
||||||
textSize = 15f
|
textSize = 15f
|
||||||
setTypeface(typeface, Typeface.BOLD)
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
|
@ -289,14 +354,22 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
physicalHintText = TextView(this).apply {
|
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())
|
setTextColor(if (mode == MODE_VIDEO) 0xFFFF3B30.toInt() else 0xFFFFC400.toInt())
|
||||||
textSize = 15f
|
textSize = 15f
|
||||||
setTypeface(typeface, Typeface.BOLD)
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateText = TextView(this).apply {
|
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)
|
setTextColor(Color.WHITE)
|
||||||
textSize = 13f
|
textSize = 13f
|
||||||
gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||||
|
|
@ -335,10 +408,10 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
if (now - lastTouchActionMs < 350) return
|
if (now - lastTouchActionMs < 350) return
|
||||||
lastTouchActionMs = now
|
lastTouchActionMs = now
|
||||||
|
|
||||||
if (mode == MODE_VIDEO) {
|
when (mode) {
|
||||||
toggleVideoRecording()
|
MODE_VIDEO -> toggleVideoRecording()
|
||||||
} else {
|
MODE_FRONT_PREVIEW -> Unit
|
||||||
takePhoto()
|
else -> takePhoto()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,7 +428,25 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
|
|
||||||
provider.unbindAll()
|
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()
|
val recorder = Recorder.Builder()
|
||||||
.setQualitySelector(
|
.setQualitySelector(
|
||||||
QualitySelector.from(
|
QualitySelector.from(
|
||||||
|
|
@ -397,7 +488,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
captureButton.isEnabled = true
|
captureButton.isEnabled = true
|
||||||
}
|
}
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
stateText.text = "Error de cámara"
|
stateText.text = "Error de camara"
|
||||||
captureButton.isEnabled = false
|
captureButton.isEnabled = false
|
||||||
}
|
}
|
||||||
}, ContextCompat.getMainExecutor(this))
|
}, ContextCompat.getMainExecutor(this))
|
||||||
|
|
@ -405,7 +496,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
|
|
||||||
private fun takePhoto() {
|
private fun takePhoto() {
|
||||||
val capture = imageCapture ?: run {
|
val capture = imageCapture ?: run {
|
||||||
stateText.text = "Cámara no lista"
|
stateText.text = "Camara no lista"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -624,7 +715,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
chronometer.visibility = View.GONE
|
chronometer.visibility = View.GONE
|
||||||
recordDot.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"
|
physicalHintText.text = "F5 VIDEO"
|
||||||
|
|
||||||
captureButton.text = "REC"
|
captureButton.text = "REC"
|
||||||
|
|
@ -641,6 +732,7 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
setBackgroundColor(Color.WHITE)
|
setBackgroundColor(Color.WHITE)
|
||||||
alpha = 0.75f
|
alpha = 0.75f
|
||||||
isClickable = false
|
isClickable = false
|
||||||
|
visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE
|
||||||
}
|
}
|
||||||
val root = findViewById<FrameLayout>(android.R.id.content)
|
val root = findViewById<FrameLayout>(android.R.id.content)
|
||||||
root.addView(
|
root.addView(
|
||||||
|
|
@ -712,3 +804,8 @@ class M530CameraActivity : ComponentActivity() {
|
||||||
private fun timestamp(): String =
|
private fun timestamp(): String =
|
||||||
SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US).format(Date())
|
SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US).format(Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
157
apply_front_preview_v2.ps1
Normal file
157
apply_front_preview_v2.ps1
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
$cameraPath = 'app\src\main\kotlin\com\bodycamera\twentyfoulabs\camera\M530CameraActivity.kt'
|
||||||
|
$mainPath = 'app\src\main\kotlin\com\bodycamera\twentyfoulabs\MainActivity.kt'
|
||||||
|
|
||||||
|
$camera = Get-Content $cameraPath -Raw
|
||||||
|
$main = Get-Content $mainPath -Raw
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# M530CameraActivity.kt
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 1. Agregar modo FRONT_PREVIEW
|
||||||
|
$camera = [regex]::Replace(
|
||||||
|
$camera,
|
||||||
|
'const val MODE_VIDEO = "video"',
|
||||||
|
'const val MODE_VIDEO = "video"' + [Environment]::NewLine +
|
||||||
|
' const val MODE_FRONT_PREVIEW = "front_preview"',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Texto superior
|
||||||
|
$camera = $camera.Replace(
|
||||||
|
'text = if (mode == MODE_VIDEO) "VIDEO" else "FOTO"',
|
||||||
|
'text = when (mode) {' + [Environment]::NewLine +
|
||||||
|
' MODE_VIDEO -> "VIDEO"' + [Environment]::NewLine +
|
||||||
|
' MODE_FRONT_PREVIEW -> "CAMARA FRONTAL"' + [Environment]::NewLine +
|
||||||
|
' else -> "FOTO"' + [Environment]::NewLine +
|
||||||
|
' }'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Ocultar botón de captura cuando sea preview frontal
|
||||||
|
$camera = $camera.Replace(
|
||||||
|
'text = if (mode == MODE_VIDEO) "REC" else "FOTO"',
|
||||||
|
'text = if (mode == MODE_VIDEO) "REC" else "FOTO"' + [Environment]::NewLine +
|
||||||
|
' visibility = if (mode == MODE_FRONT_PREVIEW) View.GONE else View.VISIBLE'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. Texto inferior
|
||||||
|
$camera = $camera.Replace(
|
||||||
|
'text = if (mode == MODE_VIDEO) "F5 VIDEO" else "F2 FOTO"',
|
||||||
|
'text = when (mode) {' + [Environment]::NewLine +
|
||||||
|
' MODE_VIDEO -> "F5 VIDEO"' + [Environment]::NewLine +
|
||||||
|
' MODE_FRONT_PREVIEW -> "VISTA EN VIVO"' + [Environment]::NewLine +
|
||||||
|
' else -> "F2 FOTO"' + [Environment]::NewLine +
|
||||||
|
' }'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 5. Estado inicial
|
||||||
|
$camera = [regex]::Replace(
|
||||||
|
$camera,
|
||||||
|
'text = if \(mode == MODE_VIDEO\) "Preparando video\.\.\." else ".*?"',
|
||||||
|
'text = when (mode) {' + [Environment]::NewLine +
|
||||||
|
' MODE_VIDEO -> "Preparando video..."' + [Environment]::NewLine +
|
||||||
|
' MODE_FRONT_PREVIEW -> "Preparando camara frontal..."' + [Environment]::NewLine +
|
||||||
|
' else -> "Preparando camara..."' + [Environment]::NewLine +
|
||||||
|
' }',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
# 6. Evitar acciones de foto/video en preview frontal
|
||||||
|
$camera = [regex]::Replace(
|
||||||
|
$camera,
|
||||||
|
'if \(mode == MODE_VIDEO\) \{\s*toggleVideoRecording\(\)\s*\} else \{\s*takePhoto\(\)\s*\}',
|
||||||
|
'when (mode) {' + [Environment]::NewLine +
|
||||||
|
' MODE_VIDEO -> toggleVideoRecording()' + [Environment]::NewLine +
|
||||||
|
' MODE_FRONT_PREVIEW -> Unit' + [Environment]::NewLine +
|
||||||
|
' else -> takePhoto()' + [Environment]::NewLine +
|
||||||
|
' }',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
# 7. Usar cámara frontal solo en FRONT_PREVIEW
|
||||||
|
$camera = [regex]::Replace(
|
||||||
|
$camera,
|
||||||
|
'(\s*provider\.unbindAll\(\)\s*)if \(mode == MODE_VIDEO\) \{',
|
||||||
|
'$1if (mode == MODE_FRONT_PREVIEW) {' + [Environment]::NewLine +
|
||||||
|
' imageCapture = null' + [Environment]::NewLine +
|
||||||
|
' videoCapture = null' + [Environment]::NewLine +
|
||||||
|
'' + [Environment]::NewLine +
|
||||||
|
' provider.bindToLifecycle(' + [Environment]::NewLine +
|
||||||
|
' this,' + [Environment]::NewLine +
|
||||||
|
' CameraSelector.DEFAULT_FRONT_CAMERA,' + [Environment]::NewLine +
|
||||||
|
' preview' + [Environment]::NewLine +
|
||||||
|
' )' + [Environment]::NewLine +
|
||||||
|
'' + [Environment]::NewLine +
|
||||||
|
' stateText.text = "Camara frontal lista"' + [Environment]::NewLine +
|
||||||
|
' captureButton.isEnabled = false' + [Environment]::NewLine +
|
||||||
|
' } else if (mode == MODE_VIDEO) {',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# MainActivity.kt
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 8. Variable para abrir la cámara frontal una sola vez por inicio
|
||||||
|
$main = $main.Replace(
|
||||||
|
'@Volatile private var isAuthenticated = false',
|
||||||
|
'@Volatile private var isAuthenticated = false' + [Environment]::NewLine +
|
||||||
|
' private var frontPreviewOpened = false'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 9. Al detectar sesión válida, abrir preview frontal
|
||||||
|
$main = [regex]::Replace(
|
||||||
|
$main,
|
||||||
|
'authSessionStore\.token\.collect \{ token -> isAuthenticated = !token\.isNullOrBlank\(\) \}',
|
||||||
|
'authSessionStore.token.collect { token ->' + [Environment]::NewLine +
|
||||||
|
' isAuthenticated = !token.isNullOrBlank()' + [Environment]::NewLine +
|
||||||
|
'' + [Environment]::NewLine +
|
||||||
|
' if (isAuthenticated && !frontPreviewOpened) {' + [Environment]::NewLine +
|
||||||
|
' frontPreviewOpened = true' + [Environment]::NewLine +
|
||||||
|
' runOnUiThread {' + [Environment]::NewLine +
|
||||||
|
' openFrontPreview()' + [Environment]::NewLine +
|
||||||
|
' }' + [Environment]::NewLine +
|
||||||
|
' }' + [Environment]::NewLine +
|
||||||
|
' }',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
# 10. Función para abrir la cámara frontal
|
||||||
|
$frontFunction = @'
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
'@
|
||||||
|
|
||||||
|
$main = $main.Replace(
|
||||||
|
' private fun handlePhotoButton() {',
|
||||||
|
$frontFunction + ' private fun handlePhotoButton() {'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Guardar cambios
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
Set-Content -Path $cameraPath -Value $camera -Encoding UTF8
|
||||||
|
Set-Content -Path $mainPath -Value $main -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host '=========================================='
|
||||||
|
Write-Host 'FRONT PREVIEW V2 APLICADO'
|
||||||
|
Write-Host '=========================================='
|
||||||
|
Write-Host 'MODE_FRONT_PREVIEW agregado'
|
||||||
|
Write-Host 'Camara frontal configurada'
|
||||||
|
Write-Host 'F2 y F5 mantienen camara trasera'
|
||||||
|
Write-Host ''
|
||||||
174
improve_camera_buttons_ui.ps1
Normal file
174
improve_camera_buttons_ui.ps1
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
$path = 'app\src\main\kotlin\com\bodycamera\twentyfoulabs\camera\M530CameraActivity.kt'
|
||||||
|
|
||||||
|
$c = Get-Content $path -Raw
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Import para fondos redondeados
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if ($c -notmatch 'import android\.graphics\.drawable\.GradientDrawable') {
|
||||||
|
$c = $c.Replace(
|
||||||
|
'import android.graphics.Typeface',
|
||||||
|
"import android.graphics.Typeface`r`nimport android.graphics.drawable.GradientDrawable"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Boton CERRAR
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
$oldClose = @'
|
||||||
|
closeButton = Button(this).apply {
|
||||||
|
text = "X"
|
||||||
|
gravity = Gravity.CENTER
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 25f
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
minWidth = 0
|
||||||
|
minHeight = 0
|
||||||
|
setPadding(0, 0, 0, 0)
|
||||||
|
background = circleDrawable(0xB0000000.toInt(), Color.WHITE, dp(1))
|
||||||
|
isClickable = true
|
||||||
|
isFocusable = true
|
||||||
|
elevation = dp(12).toFloat()
|
||||||
|
setOnClickListener {
|
||||||
|
stopRecordingIfNeeded()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.addView(
|
||||||
|
closeButton,
|
||||||
|
FrameLayout.LayoutParams(dp(54), dp(54), Gravity.TOP or Gravity.END).apply {
|
||||||
|
topMargin = dp(62)
|
||||||
|
rightMargin = dp(12)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
'@
|
||||||
|
|
||||||
|
$newClose = @'
|
||||||
|
closeButton = Button(this).apply {
|
||||||
|
text = "X"
|
||||||
|
gravity = Gravity.CENTER
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 18f
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
minWidth = 0
|
||||||
|
minHeight = 0
|
||||||
|
setPadding(0, 0, 0, 0)
|
||||||
|
|
||||||
|
background = GradientDrawable().apply {
|
||||||
|
shape = GradientDrawable.OVAL
|
||||||
|
setColor(0x99000000.toInt())
|
||||||
|
setStroke(dp(1), 0xCCFFFFFF.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
isClickable = true
|
||||||
|
isFocusable = true
|
||||||
|
elevation = dp(8).toFloat()
|
||||||
|
contentDescription = "Cerrar"
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
stopRecordingIfNeeded()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addView(
|
||||||
|
closeButton,
|
||||||
|
FrameLayout.LayoutParams(dp(44), dp(44), Gravity.TOP or Gravity.END).apply {
|
||||||
|
topMargin = dp(60)
|
||||||
|
rightMargin = dp(12)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
'@
|
||||||
|
|
||||||
|
$c = $c.Replace($oldClose, $newClose)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Boton CAMBIAR CAMARA
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
$oldSwitch = @'
|
||||||
|
switchCameraButton = Button(this).apply {
|
||||||
|
text = "CAMBIAR"
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 12f
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
gravity = Gravity.CENTER
|
||||||
|
minWidth = 0
|
||||||
|
minHeight = 0
|
||||||
|
setPadding(dp(8), 0, dp(8), 0)
|
||||||
|
background = circleDrawable(0xB0000000.toInt(), Color.WHITE, dp(1))
|
||||||
|
visibility = if (mode == MODE_FRONT_PREVIEW) View.VISIBLE else View.GONE
|
||||||
|
elevation = dp(12).toFloat()
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
if (mode == MODE_FRONT_PREVIEW) {
|
||||||
|
frontPreviewUsesFrontCamera = !frontPreviewUsesFrontCamera
|
||||||
|
startCamera()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addView(
|
||||||
|
switchCameraButton,
|
||||||
|
FrameLayout.LayoutParams(dp(110), dp(48), Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL).apply {
|
||||||
|
bottomMargin = dp(76)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
'@
|
||||||
|
|
||||||
|
$newSwitch = @'
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
'@
|
||||||
|
|
||||||
|
$c = $c.Replace($oldSwitch, $newSwitch)
|
||||||
|
|
||||||
|
Set-Content -Path $path -Value $c -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "UI DE CAMARA ACTUALIZADA"
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host ""
|
||||||
Loading…
Add table
Add a link
Reference in a new issue