package ru.omni_devel.cards import android.app.Activity import android.content.Intent import android.os.Bundle import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.LocalActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues 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.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Palette import androidx.compose.material.icons.outlined.QrCodeScanner import androidx.compose.material.icons.outlined.TextFields import androidx.compose.material.icons.outlined.Save import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.google.zxing.BarcodeFormat import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions import ru.omni_devel.cards.ui.theme.OmniCardsTheme class EditCardActivity : ComponentActivity() { private lateinit var db: DbHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() db = DbHelper(this, null) val cardId = intent.getIntExtra("cardId", -1) val cardInfo = db.getCard(cardId) setContent { OmniCardsTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> EditCardPage(cardInfo, innerPadding, getDbFun = { return@EditCardPage db }) } } } } } @Composable fun CardEditable( cardName: String, onCardNameChange: (String) -> Unit, codeValue: String, onCodeValueChange: (String) -> Unit, codeType: BarcodeFormat, onCodeTypeChange: (BarcodeFormat) -> Unit, color: String?, onColorChange: (String?) -> Unit, icon: String?, onIconChange: (String?) -> Unit ) { val context = LocalContext.current var isCodeTypeDropdownExpanded by rememberSaveable { mutableStateOf(false) } var isColorEnabled by rememberSaveable { mutableStateOf(color != null) } var currentColor by rememberSaveable { mutableStateOf(color) } val fieldModifier = Modifier.fillMaxWidth() val scanLauncher = rememberLauncherForActivityResult( contract = ScanContract() ) { result -> if (result.contents != null) { onCodeValueChange(result.contents) result.formatName?.let { formatName -> try { onCodeTypeChange(BarcodeFormat.valueOf(formatName)) } catch (_: IllegalArgumentException) {} } } } val editColorLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val color = result.data?.getStringExtra("color") currentColor = color onColorChange(color) } } val selectTemplateLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val codeValue = result.data?.getStringExtra("codeValue")!! val codeType = BarcodeFormat.valueOf(result.data?.getStringExtra("codeType")!!) onCodeValueChange(codeValue) onCodeTypeChange(codeType) } } val selectIconLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val icon = result.data?.getStringExtra("icon") onIconChange(icon) } } OutlinedTextField( value = cardName, onValueChange = onCardNameChange, modifier = fieldModifier, label = { Text(stringResource(R.string.card_name)) } ) OutlinedTextField( value = codeValue, onValueChange = onCodeValueChange, modifier = fieldModifier, label = { Text(stringResource(R.string.card_value)) } ) Box( modifier = fieldModifier, ) { OutlinedTextField( value = codeType.name, onValueChange = {}, modifier = fieldModifier, label = { Text(stringResource(R.string.card_code_type)) }, readOnly = true, ) Box( modifier = fieldModifier .matchParentSize() .clickable { isCodeTypeDropdownExpanded = !isCodeTypeDropdownExpanded } ) DropdownMenu( expanded = isCodeTypeDropdownExpanded, onDismissRequest = { isCodeTypeDropdownExpanded = false }, modifier = fieldModifier ) { BarcodeFormat.entries.forEach { format -> DropdownMenuItem( text = { Text(format.name) }, onClick = { onCodeTypeChange(format) isCodeTypeDropdownExpanded = false } ) } } } AdaptiveButton( modifier = Modifier.fillMaxWidth(), icon = Icons.Outlined.QrCodeScanner, onClick = { scanLauncher.launch( ScanOptions().apply { setPrompt(context.getString(R.string.scan_your_card)) setBeepEnabled(true) setOrientationLocked(true) } ) } ) { Text(stringResource(R.string.do_scan_card)) } AdaptiveButton( modifier = Modifier.fillMaxWidth(), icon = Icons.Outlined.TextFields, onClick = { val intent = Intent(context, SelectTemplateActivity::class.java) selectTemplateLauncher.launch(intent) } ) { Text(stringResource(R.string.card_from_template)) } Row( modifier = fieldModifier, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { AdaptiveButton( modifier = Modifier.fillMaxWidth().weight(1.0f), icon = Icons.Outlined.Palette, enabled = isColorEnabled, onClick = { val intent = Intent(context, ChooseColorActivity::class.java) intent.putExtra("currentColor", currentColor) editColorLauncher.launch(intent) } ) { Text(stringResource(R.string.do_choose_color)) } Switch( checked = isColorEnabled, onCheckedChange = { isColorEnabled = it if (!isColorEnabled) { onColorChange(null) } } ) } AdaptiveButton( modifier = Modifier.fillMaxWidth().height(64.dp), contentPadding = PaddingValues(horizontal = 0.dp), onClick = { val intent = Intent(context, SelectIconActivity::class.java) selectIconLauncher.launch(intent) } ) { Row( modifier = Modifier .fillMaxSize() .background(Color.Transparent) .padding(horizontal = 8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { CardIcon(if (icon == null) null else base64ToImageBitmap(icon), MaterialTheme.colorScheme.primary) Text(stringResource(R.string.do_select_icon)) } } } @Composable fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) { var cardName by rememberSaveable { mutableStateOf("") } var codeValue by rememberSaveable { mutableStateOf("") } var codeType by rememberSaveable { mutableStateOf(BarcodeFormat.QR_CODE) } var color by rememberSaveable { mutableStateOf(null) } var icon by rememberSaveable { mutableStateOf(null) } val context = LocalActivity.current Page( stringResource(R.string.new_card_title), innerPadding ) { Column( modifier = Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp) ) { CardEditable( cardName = cardName, onCardNameChange = { cardName = it }, codeValue = codeValue, onCodeValueChange = { codeValue = it }, codeType = codeType, onCodeTypeChange = { codeType = it }, color = color, onColorChange = { color = it }, icon = icon, onIconChange = { icon = it } ) AdaptiveButton( modifier = Modifier.fillMaxWidth(), icon = Icons.Outlined.Save, onClick = { cardName = cardName.trim() val err = checkCardData(cardName, codeValue) if (err == null) { getDbFun().addCard(cardName, codeValue, codeType, color, icon) context!!.finish() } else { Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show() } } ) { Text(stringResource(R.string.do_save)) } } } } @Composable fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> DbHelper) { var cardName by rememberSaveable { mutableStateOf(cardInfo.name) } var codeValue by rememberSaveable { mutableStateOf(cardInfo.codeValue) } var codeType by rememberSaveable { mutableStateOf(cardInfo.codeType) } var color by rememberSaveable { mutableStateOf(cardInfo.color) } var icon by rememberSaveable { mutableStateOf(cardInfo.icon) } val context = LocalActivity.current Page( stringResource(R.string.editing_card_title, cardInfo.id), innerPadding ) { Column( modifier = Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp) ) { CardEditable( cardName = cardName, onCardNameChange = { cardName = it }, codeValue = codeValue, onCodeValueChange = { codeValue = it }, codeType = codeType, onCodeTypeChange = { codeType = it }, color = color, onColorChange = { color = it }, icon = icon, onIconChange = { icon = it } ) AdaptiveButton( modifier = Modifier.fillMaxWidth(), icon = Icons.Outlined.Save, onClick = { cardName = cardName.trim() val err = checkCardData(cardName, codeValue) if (err == null) { getDbFun().editCard(cardInfo.id, cardName, codeValue, codeType, color, icon) context!!.finish() } else { Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show() } } ) { Text(stringResource(R.string.do_save)) } } } } @Composable fun EditCardPage(cardInfo: CardInfo?, innerPadding: PaddingValues, getDbFun: () -> DbHelper) { if (cardInfo == null) { AddCard(innerPadding, getDbFun) } else { EditCard(cardInfo, innerPadding, getDbFun) } }