nicer crosshair, icons instead of text buttons, share history
This commit is contained in:
@@ -6,13 +6,14 @@ import androidx.activity.compose.setContent
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import com.clean.scanner.ui.CleanScannerAppRoot
|
||||
import com.clean.scanner.ui.theme.CleanScannerTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val container = (application as CleanScannerApp).appContainer
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
CleanScannerTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
CleanScannerAppRoot(container)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.clean.scanner.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
@@ -9,17 +11,17 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.clean.scanner.AppContainer
|
||||
import com.clean.scanner.R
|
||||
import com.clean.scanner.ui.screens.HistoryScreen
|
||||
import com.clean.scanner.ui.screens.HomeScreen
|
||||
import com.clean.scanner.ui.screens.ScannerScreen
|
||||
import com.clean.scanner.ui.screens.SettingsScreen
|
||||
|
||||
private enum class RootTab { Home, History, Settings }
|
||||
private enum class RootTab { Scanner, History, Settings }
|
||||
|
||||
@Composable
|
||||
fun CleanScannerAppRoot(container: AppContainer) {
|
||||
@@ -28,17 +30,16 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
val appState by appViewModel.uiState.collectAsStateWithLifecycle()
|
||||
val scannerState by scannerViewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
var activeTab by remember { mutableStateOf(RootTab.Home) }
|
||||
var showScanner by remember { mutableStateOf(false) }
|
||||
var activeTab by remember { mutableStateOf(RootTab.Scanner) }
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
NavigationBarItem(
|
||||
selected = activeTab == RootTab.Home,
|
||||
selected = activeTab == RootTab.Scanner,
|
||||
onClick = {
|
||||
activeTab = RootTab.Home
|
||||
showScanner = false
|
||||
activeTab = RootTab.Scanner
|
||||
scannerViewModel.resumeScanning()
|
||||
},
|
||||
label = { Text(stringResource(R.string.scan)) },
|
||||
icon = {}
|
||||
@@ -47,7 +48,6 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
selected = activeTab == RootTab.History,
|
||||
onClick = {
|
||||
activeTab = RootTab.History
|
||||
showScanner = false
|
||||
},
|
||||
label = { Text(stringResource(R.string.history)) },
|
||||
icon = {}
|
||||
@@ -56,7 +56,6 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
selected = activeTab == RootTab.Settings,
|
||||
onClick = {
|
||||
activeTab = RootTab.Settings
|
||||
showScanner = false
|
||||
},
|
||||
label = { Text(stringResource(R.string.settings)) },
|
||||
icon = {}
|
||||
@@ -64,9 +63,9 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
androidx.compose.foundation.layout.Box(modifier = androidx.compose.ui.Modifier.padding(padding)) {
|
||||
when {
|
||||
showScanner -> ScannerScreen(
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
when (activeTab) {
|
||||
RootTab.Scanner -> ScannerScreen(
|
||||
analysisEnabled = scannerState.analysisEnabled,
|
||||
lastResult = scannerState.lastResult,
|
||||
warningsEnabled = appState.warningsEnabled,
|
||||
@@ -74,16 +73,7 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
onScanAgain = scannerViewModel::resumeScanning
|
||||
)
|
||||
|
||||
activeTab == RootTab.Home -> HomeScreen(
|
||||
historyEnabled = appState.historyEnabled,
|
||||
onHistoryToggle = { appViewModel.setHistoryEnabled(it, false) },
|
||||
onScanClick = {
|
||||
showScanner = true
|
||||
scannerViewModel.resumeScanning()
|
||||
}
|
||||
)
|
||||
|
||||
activeTab == RootTab.History -> HistoryScreen(
|
||||
RootTab.History -> HistoryScreen(
|
||||
query = appState.searchQuery,
|
||||
history = appState.history,
|
||||
onQueryChange = appViewModel::setQuery,
|
||||
@@ -91,7 +81,7 @@ fun CleanScannerAppRoot(container: AppContainer) {
|
||||
onClearAll = appViewModel::clearHistory
|
||||
)
|
||||
|
||||
activeTab == RootTab.Settings -> SettingsScreen(
|
||||
RootTab.Settings -> SettingsScreen(
|
||||
historyEnabled = appState.historyEnabled,
|
||||
warningsEnabled = appState.warningsEnabled,
|
||||
onHistoryToggle = appViewModel::setHistoryEnabled,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.clean.scanner.ui.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.Preview
|
||||
@@ -13,12 +15,14 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.clean.scanner.data.scanner.MlKitBarcodeAnalyzer
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
@Composable
|
||||
@@ -34,6 +38,21 @@ fun CameraPreview(
|
||||
val cameraExecutor: ExecutorService = remember { Executors.newSingleThreadExecutor() }
|
||||
val previewView = remember { PreviewView(context) }
|
||||
val cameraRef = remember { mutableStateOf<androidx.camera.core.Camera?>(null) }
|
||||
val zoomRatio = remember { mutableStateOf(1f) }
|
||||
|
||||
val scaleGestureDetector = remember {
|
||||
ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
val camera = cameraRef.value ?: return false
|
||||
val zoomState = camera.cameraInfo.zoomState.value ?: return false
|
||||
val nextZoom = zoomRatio.value * detector.scaleFactor
|
||||
val clampedZoom = max(zoomState.minZoomRatio, min(nextZoom, zoomState.maxZoomRatio))
|
||||
zoomRatio.value = clampedZoom
|
||||
camera.cameraControl.setZoomRatio(clampedZoom)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
@@ -70,6 +89,7 @@ fun CameraPreview(
|
||||
|
||||
onTorchAvailabilityChanged(camera.cameraInfo.hasFlashUnit())
|
||||
cameraRef.value = camera
|
||||
zoomRatio.value = camera.cameraInfo.zoomState.value?.zoomRatio ?: 1f
|
||||
}
|
||||
|
||||
LaunchedEffect(torchEnabled) {
|
||||
@@ -78,6 +98,16 @@ fun CameraPreview(
|
||||
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = { previewView }
|
||||
factory = {
|
||||
previewView.apply {
|
||||
setOnTouchListener { _, event ->
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
performClick()
|
||||
}
|
||||
scaleGestureDetector.onTouchEvent(event)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.clean.scanner.ui.screens
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -19,10 +21,12 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.clean.scanner.R
|
||||
import com.clean.scanner.domain.ScanRecord
|
||||
import com.clean.scanner.util.Intents
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
|
||||
@@ -34,7 +38,9 @@ fun HistoryScreen(
|
||||
onDelete: (Long) -> Unit,
|
||||
onClearAll: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val showDeleteAll = remember { mutableStateOf(false) }
|
||||
val selectedItem = remember { mutableStateOf<ScanRecord?>(null) }
|
||||
|
||||
if (showDeleteAll.value) {
|
||||
AlertDialog(
|
||||
@@ -55,6 +61,20 @@ fun HistoryScreen(
|
||||
)
|
||||
}
|
||||
|
||||
val detail = selectedItem.value
|
||||
if (detail != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { selectedItem.value = null },
|
||||
title = { Text(text = detail.type) },
|
||||
text = { Text(text = detail.content) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { selectedItem.value = null }) {
|
||||
Text(text = stringResource(R.string.confirm))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -68,13 +88,28 @@ fun HistoryScreen(
|
||||
label = { Text(stringResource(R.string.search)) }
|
||||
)
|
||||
|
||||
TextButton(onClick = { showDeleteAll.value = true }) {
|
||||
Text(stringResource(R.string.delete_all))
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
val exportText = buildHistoryExportText(history)
|
||||
Intents.shareText(context, exportText)
|
||||
},
|
||||
enabled = history.isNotEmpty()
|
||||
) {
|
||||
Text(stringResource(R.string.share_history))
|
||||
}
|
||||
TextButton(onClick = { showDeleteAll.value = true }) {
|
||||
Text(stringResource(R.string.delete_all))
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn {
|
||||
items(history, key = { it.id }) { item ->
|
||||
HistoryRow(item = item, onDelete = onDelete)
|
||||
HistoryRow(
|
||||
item = item,
|
||||
onDelete = onDelete,
|
||||
onOpenDetails = { selectedItem.value = item }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +117,11 @@ fun HistoryScreen(
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun HistoryRow(item: ScanRecord, onDelete: (Long) -> Unit) {
|
||||
private fun HistoryRow(
|
||||
item: ScanRecord,
|
||||
onDelete: (Long) -> Unit,
|
||||
onOpenDetails: () -> Unit
|
||||
) {
|
||||
val dismissState = rememberSwipeToDismissBoxState(
|
||||
confirmValueChange = {
|
||||
if (it == SwipeToDismissBoxValue.EndToStart || it == SwipeToDismissBoxValue.StartToEnd) {
|
||||
@@ -100,6 +139,7 @@ private fun HistoryRow(item: ScanRecord, onDelete: (Long) -> Unit) {
|
||||
content = {
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onOpenDetails() }
|
||||
.padding(vertical = 12.dp)) {
|
||||
Text(text = item.type)
|
||||
Text(text = item.content, maxLines = 2)
|
||||
@@ -108,3 +148,12 @@ private fun HistoryRow(item: ScanRecord, onDelete: (Long) -> Unit) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildHistoryExportText(history: List<ScanRecord>): String {
|
||||
if (history.isEmpty()) return ""
|
||||
val formatter = DateFormat.getDateTimeInstance()
|
||||
return history.joinToString(separator = "\n\n") { item ->
|
||||
val time = formatter.format(Date(item.timestamp))
|
||||
"$time\n${item.type}\n${item.content}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,15 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -24,6 +28,21 @@ fun HomeScreen(
|
||||
onHistoryToggle: (Boolean) -> Unit,
|
||||
onScanClick: () -> Unit
|
||||
) {
|
||||
val showPrivacyDialog = remember { mutableStateOf(false) }
|
||||
|
||||
if (showPrivacyDialog.value) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showPrivacyDialog.value = false },
|
||||
title = { Text(text = stringResource(R.string.privacy)) },
|
||||
text = { Text(text = stringResource(R.string.privacy_text)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showPrivacyDialog.value = false }) {
|
||||
Text(text = stringResource(R.string.confirm))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -47,13 +66,12 @@ fun HomeScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.privacy),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.privacy_text),
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
TextButton(onClick = { showPrivacyDialog.value = true }) {
|
||||
Text(
|
||||
text = stringResource(R.string.privacy),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,27 @@ import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
@@ -31,8 +41,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
@@ -96,12 +109,42 @@ fun ScannerScreen(
|
||||
}
|
||||
)
|
||||
|
||||
Box(
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.fillMaxWidth(0.7f)
|
||||
.height(220.dp)
|
||||
.background(Color.Transparent)
|
||||
) {
|
||||
val guideColor = Color(0xFF7CE6C6)
|
||||
val cx = size.width / 2f
|
||||
val cy = size.height / 2f
|
||||
drawRoundRect(
|
||||
color = guideColor.copy(alpha = 0.10f),
|
||||
cornerRadius = CornerRadius(26f, 26f)
|
||||
)
|
||||
drawRoundRect(
|
||||
color = guideColor.copy(alpha = 0.90f),
|
||||
cornerRadius = CornerRadius(26f, 26f),
|
||||
style = Stroke(width = 4f)
|
||||
)
|
||||
drawCircle(
|
||||
color = guideColor.copy(alpha = 0.90f),
|
||||
radius = 8f,
|
||||
center = androidx.compose.ui.geometry.Offset(cx, cy)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(R.string.pinch_to_zoom_hint),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 40.dp)
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.35f),
|
||||
shape = RoundedCornerShape(18.dp)
|
||||
)
|
||||
.padding(horizontal = 14.dp, vertical = 8.dp)
|
||||
)
|
||||
|
||||
if (torchAvailable) {
|
||||
@@ -126,7 +169,7 @@ fun ScannerScreen(
|
||||
}
|
||||
|
||||
if (lastResult != null) {
|
||||
ModalBottomSheet(onDismissRequest = {}) {
|
||||
ModalBottomSheet(onDismissRequest = onScanAgain) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -135,28 +178,38 @@ fun ScannerScreen(
|
||||
) {
|
||||
Text(text = "${stringResource(R.string.content_type)}: ${lastResult.type}")
|
||||
Text(text = "${stringResource(R.string.content_value)}: ${lastResult.content}")
|
||||
Button(onClick = { ClipboardUtil.copy(context, lastResult.content) }) {
|
||||
Text(stringResource(R.string.copy))
|
||||
}
|
||||
if (lastResult.type == "URL") {
|
||||
Button(onClick = {
|
||||
val risk = UrlRiskScorer.score(lastResult.content)
|
||||
val risky = warningsEnabled && risk.score >= 3
|
||||
if (risky) {
|
||||
pendingOpenUrl = lastResult.content
|
||||
showRiskWarning = true
|
||||
} else {
|
||||
Intents.openUrl(context, lastResult.content)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.open))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
IconButton(onClick = { ClipboardUtil.copy(context, lastResult.content) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ContentCopy,
|
||||
contentDescription = stringResource(R.string.copy)
|
||||
)
|
||||
}
|
||||
if (lastResult.type == "URL") {
|
||||
Button(onClick = {
|
||||
val risk = UrlRiskScorer.score(lastResult.content)
|
||||
val risky = warningsEnabled && risk.score >= 3
|
||||
if (risky) {
|
||||
pendingOpenUrl = lastResult.content
|
||||
showRiskWarning = true
|
||||
} else {
|
||||
Intents.openUrl(context, lastResult.content)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.open))
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { Intents.shareText(context, lastResult.content) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
contentDescription = stringResource(R.string.share)
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(onClick = { Intents.shareText(context, lastResult.content) }) {
|
||||
Text(stringResource(R.string.share))
|
||||
}
|
||||
Button(onClick = onScanAgain) {
|
||||
Text(stringResource(R.string.scan_again))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.clean.scanner.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val LightColors = lightColorScheme()
|
||||
private val DarkColors = darkColorScheme()
|
||||
|
||||
@Composable
|
||||
fun CleanScannerTheme(content: @Composable () -> Unit) {
|
||||
val darkTheme = isSystemInDarkTheme()
|
||||
val context = LocalContext.current
|
||||
|
||||
val colorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
} else {
|
||||
if (darkTheme) DarkColors else LightColors
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@@ -31,4 +31,6 @@
|
||||
<string name="content_type">Typ</string>
|
||||
<string name="content_value">Inhalt</string>
|
||||
<string name="request_camera">Kamera erlauben</string>
|
||||
<string name="pinch_to_zoom_hint">Zum Zoomen bei kleinen Codes mit zwei Fingern aufziehen</string>
|
||||
<string name="share_history">Historie teilen</string>
|
||||
</resources>
|
||||
|
||||
@@ -31,4 +31,6 @@
|
||||
<string name="content_type">Type</string>
|
||||
<string name="content_value">Content</string>
|
||||
<string name="request_camera">Allow camera</string>
|
||||
<string name="pinch_to_zoom_hint">Pinch to zoom for small codes</string>
|
||||
<string name="share_history">Share history</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user