forked from omni/OmniCards
refactor: rewrite mobile app in Compose and add error handling for wear
This commit is contained in:
@@ -1,124 +1,249 @@
|
||||
package ru.omni_devel.cards
|
||||
|
||||
import android.content.Intent
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
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.material3.Button
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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 : AppCompatActivity() {
|
||||
class EditCardActivity : ComponentActivity() {
|
||||
private lateinit var db: DbHelper
|
||||
|
||||
private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
|
||||
if (result.contents != null) {
|
||||
setCodeType(result.formatName)
|
||||
|
||||
findViewById<EditText>(R.id.cardValueInput).setText(result.contents)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_edit_card)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
val actionName = intent.getStringExtra("action") ?: EditCardAction.ADD.name
|
||||
val action = EditCardAction.valueOf(actionName)
|
||||
|
||||
db = DbHelper(this, null)
|
||||
|
||||
val actionLabel: TextView = findViewById(R.id.editCardActionLabel)
|
||||
val cardNameInput: EditText = findViewById(R.id.cardNameInput)
|
||||
val cardValueInput: EditText = findViewById(R.id.cardValueInput)
|
||||
val scanCodeButton: Button = findViewById(R.id.scanCodeButton)
|
||||
val cardCodeTypeSelector: Spinner = findViewById(R.id.cardCodeTypeSelector)
|
||||
val saveCardButton: Button = findViewById(R.id.saveCardButton)
|
||||
val cardId = intent.getIntExtra("cardId", -1)
|
||||
val cardInfo = db.getCard(cardId)
|
||||
|
||||
val options = BarcodeFormat.entries.map { it.name }
|
||||
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, options)
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
cardCodeTypeSelector.adapter = adapter
|
||||
|
||||
scanCodeButton.setOnClickListener {
|
||||
val options = ScanOptions()
|
||||
options.setPrompt("Scan code")
|
||||
options.setBeepEnabled(false)
|
||||
options.setOrientationLocked(false)
|
||||
barcodeLauncher.launch(options)
|
||||
}
|
||||
|
||||
saveCardButton.setOnClickListener {
|
||||
val cardName = cardNameInput.text.toString()
|
||||
val cardValue = cardValueInput.text.toString()
|
||||
val cardCodeType = BarcodeFormat.valueOf(cardCodeTypeSelector.selectedItem.toString())
|
||||
|
||||
when (action) {
|
||||
EditCardAction.ADD -> {
|
||||
db.addCard(cardName, cardValue, cardCodeType)
|
||||
|
||||
finish()
|
||||
}
|
||||
EditCardAction.EDIT -> {
|
||||
val cardId = intent.getIntExtra("cardId", -1)
|
||||
|
||||
db.editCard(cardId, cardName, cardValue, cardCodeType)
|
||||
|
||||
val intent = Intent(this, ShowCardActivity::class.java)
|
||||
|
||||
intent.putExtra("cardId", cardId)
|
||||
|
||||
startActivity(intent)
|
||||
finish()
|
||||
setContent {
|
||||
OmniCardsTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
EditCardPage(cardInfo, innerPadding, getDbFun = {
|
||||
return@EditCardPage db
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (action) {
|
||||
EditCardAction.ADD -> {
|
||||
setCodeType("QR_CODE")
|
||||
@Composable
|
||||
fun CardEditable(
|
||||
cardName: String,
|
||||
onCardNameChange: (String) -> Unit,
|
||||
codeValue: String,
|
||||
oncodeValueChange: (String) -> Unit,
|
||||
codeType: BarcodeFormat,
|
||||
onCodeTypeChange: (BarcodeFormat) -> Unit
|
||||
) {
|
||||
var isCodeTypeDropdownExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
actionLabel.text = "Adding card"
|
||||
}
|
||||
EditCardAction.EDIT -> {
|
||||
val cardId = intent.getIntExtra("cardId", -1)
|
||||
val fieldModifier = Modifier.fillMaxWidth()
|
||||
|
||||
val cardInfo = db.getCard(cardId)
|
||||
val scanLauncher = rememberLauncherForActivityResult(
|
||||
contract = ScanContract()
|
||||
) { result ->
|
||||
if (result.contents != null) {
|
||||
oncodeValueChange(result.contents)
|
||||
|
||||
if (cardInfo == null) {
|
||||
Toast.makeText(this, "cardInfo is null", Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
|
||||
actionLabel.text = "Editing card"
|
||||
|
||||
cardNameInput.setText(cardInfo.name)
|
||||
cardValueInput.setText(cardInfo.codeValue)
|
||||
setCodeType(cardInfo.codeType.name)
|
||||
result.formatName?.let { formatName ->
|
||||
try {
|
||||
onCodeTypeChange(BarcodeFormat.valueOf(formatName))
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCodeType(codeTypeString: String) {
|
||||
val codeTypes = BarcodeFormat.entries.map { it.name }
|
||||
val codeTypeIndex = codeTypes.indexOf(codeTypeString)
|
||||
|
||||
if (codeTypeIndex >= 0) {
|
||||
findViewById<Spinner>(R.id.cardCodeTypeSelector).setSelection(codeTypeIndex)
|
||||
OutlinedTextField(
|
||||
value = cardName,
|
||||
onValueChange = onCardNameChange,
|
||||
modifier = fieldModifier,
|
||||
label = { Text("Name") }
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = codeValue,
|
||||
onValueChange = oncodeValueChange,
|
||||
modifier = Modifier.weight(1f),
|
||||
label = { Text("Value") }
|
||||
)
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
onClick = {
|
||||
scanLauncher.launch(
|
||||
ScanOptions().apply {
|
||||
setPrompt("Scan your card")
|
||||
setBeepEnabled(true)
|
||||
setOrientationLocked(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text("Scan")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = fieldModifier,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = codeType.name,
|
||||
onValueChange = {},
|
||||
modifier = fieldModifier,
|
||||
label = { Text("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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
||||
var cardName by remember { mutableStateOf("") }
|
||||
var codeValue by remember { mutableStateOf("") }
|
||||
var codeType by remember { mutableStateOf(BarcodeFormat.QR_CODE) }
|
||||
|
||||
val context = LocalContext.current as Activity
|
||||
|
||||
Page(
|
||||
"New card",
|
||||
innerPadding
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
CardEditable(
|
||||
cardName = cardName,
|
||||
onCardNameChange = { cardName = it },
|
||||
codeValue = codeValue,
|
||||
oncodeValueChange = { codeValue = it },
|
||||
codeType = codeType,
|
||||
onCodeTypeChange = { codeType = it }
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
val err = checkCardData(cardName, codeValue)
|
||||
|
||||
if (err == null) {
|
||||
getDbFun().addCard(cardName, codeValue, codeType)
|
||||
context.finish()
|
||||
} else {
|
||||
Toast.makeText(context, err, Toast.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
||||
var cardName by remember { mutableStateOf(cardInfo.name) }
|
||||
var codeValue by remember { mutableStateOf(cardInfo.codeValue) }
|
||||
var codeType by remember { mutableStateOf(cardInfo.codeType) }
|
||||
|
||||
val context = LocalContext.current as Activity
|
||||
|
||||
Page(
|
||||
"Editing card id${cardInfo.id}",
|
||||
innerPadding
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
CardEditable(
|
||||
cardName = cardName,
|
||||
onCardNameChange = { cardName = it },
|
||||
codeValue = codeValue,
|
||||
oncodeValueChange = { codeValue = it },
|
||||
codeType = codeType,
|
||||
onCodeTypeChange = { codeType = it }
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
val err = checkCardData(cardName, codeValue)
|
||||
|
||||
if (err == null) {
|
||||
getDbFun().editCard(cardInfo.id, cardName, codeValue, codeType)
|
||||
context.finish()
|
||||
} else {
|
||||
Toast.makeText(context, err, Toast.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditCardPage(cardInfo: CardInfo?, innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
||||
if (cardInfo == null) {
|
||||
AddCard(innerPadding, getDbFun)
|
||||
} else {
|
||||
EditCard(cardInfo, innerPadding, getDbFun)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user