New feature: flashlight (#61)
This commit is contained in:
@@ -18,6 +18,7 @@ import android.graphics.Bitmap
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import androidx.camera.core.CameraControl
|
||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import androidx.camera.core.ImageAnalysis
|
import androidx.camera.core.ImageAnalysis
|
||||||
import androidx.camera.core.ImageCapture
|
import androidx.camera.core.ImageCapture
|
||||||
@@ -136,7 +137,9 @@ fun bindCameraUseCases(
|
|||||||
captureController.imageCapture = imageCapture
|
captureController.imageCapture = imageCapture
|
||||||
|
|
||||||
val cameraProvider = cameraProviderFuture.get()
|
val cameraProvider = cameraProviderFuture.get()
|
||||||
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis, preview, imageCapture)
|
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector,
|
||||||
|
imageAnalysis, preview, imageCapture)
|
||||||
|
captureController.cameraControl = camera.cameraControl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -197,6 +200,7 @@ fun replaceColor(bitmap: Bitmap, toReplace: Color, replacement: Color): Bitmap {
|
|||||||
fun Point.toOffset() = Offset(x.toFloat(), y.toFloat())
|
fun Point.toOffset() = Offset(x.toFloat(), y.toFloat())
|
||||||
|
|
||||||
class CameraCaptureController {
|
class CameraCaptureController {
|
||||||
|
var cameraControl: CameraControl? = null
|
||||||
var imageCapture: ImageCapture? = null
|
var imageCapture: ImageCapture? = null
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Done
|
import androidx.compose.material.icons.filled.Done
|
||||||
|
import androidx.compose.material.icons.filled.FlashOff
|
||||||
|
import androidx.compose.material.icons.filled.FlashOn
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -93,7 +97,8 @@ data class CameraUiState(
|
|||||||
val captureState: CaptureState,
|
val captureState: CaptureState,
|
||||||
val showDetectionError: Boolean,
|
val showDetectionError: Boolean,
|
||||||
val isLandscape: Boolean,
|
val isLandscape: Boolean,
|
||||||
val isDebugMode: Boolean
|
val isDebugMode: Boolean,
|
||||||
|
val isTorchEnabled: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
const val CAPTURED_IMAGE_DISPLAY_DURATION = 1500L
|
const val CAPTURED_IMAGE_DISPLAY_DURATION = 1500L
|
||||||
@@ -112,6 +117,7 @@ fun CameraScreen(
|
|||||||
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
||||||
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
|
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
|
||||||
var isDebugMode by remember { mutableStateOf(false) }
|
var isDebugMode by remember { mutableStateOf(false) }
|
||||||
|
var isTorchEnabled by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
BackHandler { navigation.back() }
|
BackHandler { navigation.back() }
|
||||||
|
|
||||||
@@ -168,7 +174,8 @@ fun CameraScreen(
|
|||||||
captureState,
|
captureState,
|
||||||
showDetectionError,
|
showDetectionError,
|
||||||
isLandscape = isLandscape,
|
isLandscape = isLandscape,
|
||||||
isDebugMode),
|
isDebugMode,
|
||||||
|
isTorchEnabled),
|
||||||
onCapture = {
|
onCapture = {
|
||||||
previewView?.bitmap?.let {
|
previewView?.bitmap?.let {
|
||||||
Log.i("FairScan", "Pressed <Capture>")
|
Log.i("FairScan", "Pressed <Capture>")
|
||||||
@@ -180,6 +187,9 @@ fun CameraScreen(
|
|||||||
},
|
},
|
||||||
onFinalizePressed = onFinalizePressed,
|
onFinalizePressed = onFinalizePressed,
|
||||||
onDebugModeSwitched = { isDebugMode = !isDebugMode },
|
onDebugModeSwitched = { isDebugMode = !isDebugMode },
|
||||||
|
onTorchSwitched = {
|
||||||
|
isTorchEnabled = !isTorchEnabled
|
||||||
|
captureController.cameraControl?.enableTorch(isTorchEnabled) },
|
||||||
thumbnailCoords = thumbnailCoords,
|
thumbnailCoords = thumbnailCoords,
|
||||||
navigation = navigation
|
navigation = navigation
|
||||||
)
|
)
|
||||||
@@ -193,6 +203,7 @@ private fun CameraScreenScaffold(
|
|||||||
onCapture: () -> Unit,
|
onCapture: () -> Unit,
|
||||||
onFinalizePressed: () -> Unit,
|
onFinalizePressed: () -> Unit,
|
||||||
onDebugModeSwitched: () -> Unit,
|
onDebugModeSwitched: () -> Unit,
|
||||||
|
onTorchSwitched: () -> Unit,
|
||||||
thumbnailCoords: MutableState<Offset>,
|
thumbnailCoords: MutableState<Offset>,
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
) {
|
) {
|
||||||
@@ -225,6 +236,7 @@ private fun CameraScreenScaffold(
|
|||||||
cameraPreview,
|
cameraPreview,
|
||||||
cameraUiState,
|
cameraUiState,
|
||||||
onCapture,
|
onCapture,
|
||||||
|
onTorchSwitched,
|
||||||
modifier.clickable(onClick = onPageCountClick))
|
modifier.clickable(onClick = onPageCountClick))
|
||||||
}
|
}
|
||||||
if (cameraUiState.captureState is CaptureState.CapturePreview) {
|
if (cameraUiState.captureState is CaptureState.CapturePreview) {
|
||||||
@@ -238,6 +250,7 @@ private fun CameraPreviewBox(
|
|||||||
cameraPreview: @Composable (() -> Unit),
|
cameraPreview: @Composable (() -> Unit),
|
||||||
cameraUiState: CameraUiState,
|
cameraUiState: CameraUiState,
|
||||||
onCapture: () -> Unit,
|
onCapture: () -> Unit,
|
||||||
|
onTorchSwitched: () -> Unit,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
@@ -257,6 +270,20 @@ private fun CameraPreviewBox(
|
|||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
)
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = onTorchSwitched,
|
||||||
|
modifier = Modifier.align(Alignment.BottomStart)
|
||||||
|
) {
|
||||||
|
val torchEnabled = cameraUiState.isTorchEnabled
|
||||||
|
val icon = if (torchEnabled) Icons.Default.FlashOn else Icons.Default.FlashOff
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription =
|
||||||
|
stringResource(
|
||||||
|
if (torchEnabled) R.string.turn_off_torch else R.string.turn_on_torch),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,10 +506,11 @@ private fun ScreenPreview(captureState: CaptureState, rotationDegrees: Float = 0
|
|||||||
listState = LazyListState(),
|
listState = LazyListState(),
|
||||||
),
|
),
|
||||||
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState,
|
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState,
|
||||||
false, rotationDegrees > 0, false),
|
false, rotationDegrees > 0, false, false),
|
||||||
onCapture = {},
|
onCapture = {},
|
||||||
onFinalizePressed = {},
|
onFinalizePressed = {},
|
||||||
onDebugModeSwitched = {},
|
onDebugModeSwitched = {},
|
||||||
|
onTorchSwitched = {},
|
||||||
thumbnailCoords = thumbnailCoords,
|
thumbnailCoords = thumbnailCoords,
|
||||||
navigation = dummyNavigation()
|
navigation = dummyNavigation()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
<string name="share">Sdílet</string>
|
<string name="share">Sdílet</string>
|
||||||
<string name="share_pdf">Sdílet PDF</string>
|
<string name="share_pdf">Sdílet PDF</string>
|
||||||
<string name="storage_permission_denied">Nelze uložit PDF: přístup zakázán</string>
|
<string name="storage_permission_denied">Nelze uložit PDF: přístup zakázán</string>
|
||||||
|
<string name="turn_off_torch">Vypnout svítilnu</string>
|
||||||
|
<string name="turn_on_torch">Zapnout svítilnu</string>
|
||||||
<string name="unknown_size">Neznámá velikost</string>
|
<string name="unknown_size">Neznámá velikost</string>
|
||||||
<string name="version">Verze</string>
|
<string name="version">Verze</string>
|
||||||
<string name="view_the_full_license">Zobrazit úplnou licenci</string>
|
<string name="view_the_full_license">Zobrazit úplnou licenci</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Teilen</string>
|
<string name="share">Teilen</string>
|
||||||
<string name="share_pdf">PDF teilen</string>
|
<string name="share_pdf">PDF teilen</string>
|
||||||
<string name="storage_permission_denied">PDF-Datei kann nicht gespeichert werden: Berechtigung verweigert</string>
|
<string name="storage_permission_denied">PDF-Datei kann nicht gespeichert werden: Berechtigung verweigert</string>
|
||||||
|
<string name="turn_off_torch">Taschenlampe ausschalten</string>
|
||||||
|
<string name="turn_on_torch">Taschenlampe einschalten</string>
|
||||||
<string name="unknown_size">Unbekannte Größe</string>
|
<string name="unknown_size">Unbekannte Größe</string>
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
<string name="view_the_full_license">Vollständige Lizenz anzeigen</string>
|
<string name="view_the_full_license">Vollständige Lizenz anzeigen</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Compartir</string>
|
<string name="share">Compartir</string>
|
||||||
<string name="share_pdf">Compartir PDF</string>
|
<string name="share_pdf">Compartir PDF</string>
|
||||||
<string name="storage_permission_denied">No se puede guardar el archivo PDF: permiso denegado</string>
|
<string name="storage_permission_denied">No se puede guardar el archivo PDF: permiso denegado</string>
|
||||||
|
<string name="turn_off_torch">Apagar linterna</string>
|
||||||
|
<string name="turn_on_torch">Encender linterna</string>
|
||||||
<string name="unknown_size">Tamaño desconocido</string>
|
<string name="unknown_size">Tamaño desconocido</string>
|
||||||
<string name="version">Versión</string>
|
<string name="version">Versión</string>
|
||||||
<string name="view_the_full_license">Ver la licencia completa</string>
|
<string name="view_the_full_license">Ver la licencia completa</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Partager</string>
|
<string name="share">Partager</string>
|
||||||
<string name="share_pdf">Partager le PDF</string>
|
<string name="share_pdf">Partager le PDF</string>
|
||||||
<string name="storage_permission_denied">Impossible d’enregistrer le fichier PDF : permission refusée</string>
|
<string name="storage_permission_denied">Impossible d’enregistrer le fichier PDF : permission refusée</string>
|
||||||
|
<string name="turn_off_torch">Éteindre la torche</string>
|
||||||
|
<string name="turn_on_torch">Allumer la torche</string>
|
||||||
<string name="unknown_size">Taille inconnue</string>
|
<string name="unknown_size">Taille inconnue</string>
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
<string name="view_the_full_license">Voir la licence complète</string>
|
<string name="view_the_full_license">Voir la licence complète</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Condividi</string>
|
<string name="share">Condividi</string>
|
||||||
<string name="share_pdf">Condividi PDF</string>
|
<string name="share_pdf">Condividi PDF</string>
|
||||||
<string name="storage_permission_denied">Impossibile salvare il file PDF: autorizzazione negata</string>
|
<string name="storage_permission_denied">Impossibile salvare il file PDF: autorizzazione negata</string>
|
||||||
|
<string name="turn_off_torch">Spegni la torcia</string>
|
||||||
|
<string name="turn_on_torch">Accendi la torcia</string>
|
||||||
<string name="unknown_size">Dimensione sconosciuta</string>
|
<string name="unknown_size">Dimensione sconosciuta</string>
|
||||||
<string name="version">Versione</string>
|
<string name="version">Versione</string>
|
||||||
<string name="view_the_full_license">Vedi la licenza completa</string>
|
<string name="view_the_full_license">Vedi la licenza completa</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Compartilhar</string>
|
<string name="share">Compartilhar</string>
|
||||||
<string name="share_pdf">Compartilhar PDF</string>
|
<string name="share_pdf">Compartilhar PDF</string>
|
||||||
<string name="storage_permission_denied">Não foi possível salvar o arquivo PDF: permissão negada</string>
|
<string name="storage_permission_denied">Não foi possível salvar o arquivo PDF: permissão negada</string>
|
||||||
|
<string name="turn_off_torch">Desligar lanterna</string>
|
||||||
|
<string name="turn_on_torch">Ligar lanterna</string>
|
||||||
<string name="unknown_size">Tamanho desconhecido</string>
|
<string name="unknown_size">Tamanho desconhecido</string>
|
||||||
<string name="version">Versão</string>
|
<string name="version">Versão</string>
|
||||||
<string name="view_the_full_license">Ver licença completa</string>
|
<string name="view_the_full_license">Ver licença completa</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">Поделиться</string>
|
<string name="share">Поделиться</string>
|
||||||
<string name="share_pdf">Поделиться PDF</string>
|
<string name="share_pdf">Поделиться PDF</string>
|
||||||
<string name="storage_permission_denied">Не удается сохранить файл PDF: в разрешении отказано</string>
|
<string name="storage_permission_denied">Не удается сохранить файл PDF: в разрешении отказано</string>
|
||||||
|
<string name="turn_off_torch">Выключить фонарик</string>
|
||||||
|
<string name="turn_on_torch">Включить фонарик</string>
|
||||||
<string name="unknown_size">Неизвестный размер</string>
|
<string name="unknown_size">Неизвестный размер</string>
|
||||||
<string name="version">Версия</string>
|
<string name="version">Версия</string>
|
||||||
<string name="view_the_full_license">Просмотреть полную лицензию</string>
|
<string name="view_the_full_license">Просмотреть полную лицензию</string>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
<string name="share">共享</string>
|
<string name="share">共享</string>
|
||||||
<string name="share_pdf">共享 PDF</string>
|
<string name="share_pdf">共享 PDF</string>
|
||||||
<string name="storage_permission_denied">无法保存PDF文件:权限被拒绝</string>
|
<string name="storage_permission_denied">无法保存PDF文件:权限被拒绝</string>
|
||||||
|
<string name="turn_off_torch">关闭手电筒</string>
|
||||||
|
<string name="turn_on_torch">打开手电筒</string>
|
||||||
<string name="unknown_size">未知大小</string>
|
<string name="unknown_size">未知大小</string>
|
||||||
<string name="version">版本</string>
|
<string name="version">版本</string>
|
||||||
<string name="view_the_full_license">查看完整许可证</string>
|
<string name="view_the_full_license">查看完整许可证</string>
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
<string name="share">Share</string>
|
<string name="share">Share</string>
|
||||||
<string name="share_pdf">Share PDF</string>
|
<string name="share_pdf">Share PDF</string>
|
||||||
<string name="storage_permission_denied">Cannot save PDF file: permission was denied</string>
|
<string name="storage_permission_denied">Cannot save PDF file: permission was denied</string>
|
||||||
|
<string name="turn_off_torch">Turn off torch</string>
|
||||||
|
<string name="turn_on_torch">Turn on torch</string>
|
||||||
<string name="unknown_size">Unknown size</string>
|
<string name="unknown_size">Unknown size</string>
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
<string name="view_the_full_license">View the full license</string>
|
<string name="view_the_full_license">View the full license</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user