refactor: rewrite mobile app in Compose and add error handling for wear
This commit is contained in:
@@ -2,55 +2,64 @@ package ru.omni_devel.cards
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
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 com.google.android.material.button.MaterialButton
|
||||
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.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var cardsList: LinearLayout
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var db: DbHelper
|
||||
|
||||
private val cards = mutableStateListOf<CardInfo>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_main)
|
||||
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
|
||||
}
|
||||
|
||||
db = DbHelper(this, null)
|
||||
cardsList = findViewById(R.id.cardsList)
|
||||
|
||||
val addCardButton: Button = findViewById(R.id.addCardButton)
|
||||
val syncWithWearOSButton: Button = findViewById(R.id.syncWithWearOSButton)
|
||||
|
||||
addCardButton.setOnClickListener {
|
||||
val intent = Intent(this, EditCardActivity::class.java)
|
||||
|
||||
intent.putExtra("action", EditCardAction.ADD.name)
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
syncWithWearOSButton.setOnClickListener {
|
||||
Toast.makeText(this, "Sync in progress...", Toast.LENGTH_SHORT).show()
|
||||
|
||||
sendToWatch(this, "/updateCards", Json.encodeToString(db.getCards())) { isSuccess ->
|
||||
if (isSuccess) {
|
||||
Toast.makeText(this, "Successfully synced!", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Sync failed! Please, check your connection", Toast.LENGTH_LONG).show()
|
||||
setContent {
|
||||
OmniCardsTheme {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { innerPadding ->
|
||||
MainPage(innerPadding, cards, onDeleteCard = { cardId ->
|
||||
db.removeCard(cardId)
|
||||
cards.removeIf { it.id == cardId }
|
||||
}, getDbFun = {
|
||||
return@MainPage db
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,84 +67,102 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
renderCards()
|
||||
|
||||
cards.clear()
|
||||
cards.addAll(db.getCards())
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderCards() {
|
||||
val cards = db.getCards()
|
||||
@Composable
|
||||
fun MainPage(
|
||||
innerPadding: PaddingValues,
|
||||
cards: List<CardInfo>,
|
||||
onDeleteCard: (Int) -> Unit,
|
||||
getDbFun: () -> DbHelper
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
cardsList.removeAllViews()
|
||||
var isActionBarShowed by remember { mutableStateOf(false) }
|
||||
|
||||
if (cards.isEmpty()) {
|
||||
val text = TextView(this)
|
||||
|
||||
val params = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
text.text = "Add a card to get started"
|
||||
text.layoutParams = params
|
||||
text.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
|
||||
cardsList.addView(text)
|
||||
} else {
|
||||
for (card in cards) {
|
||||
val button = MaterialButton(this)
|
||||
|
||||
val params = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
params.setMargins(16, 4, 16, 4)
|
||||
|
||||
button.text = card.name
|
||||
button.layoutParams = params
|
||||
button.cornerRadius = (12 * resources.displayMetrics.density).toInt()
|
||||
|
||||
button.setOnClickListener {
|
||||
val intent = Intent(this, ShowCardActivity::class.java)
|
||||
|
||||
intent.putExtra("cardId", card.id)
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
button.setOnLongClickListener { view ->
|
||||
val popup = PopupMenu(this, view)
|
||||
|
||||
popup.menu.add("Edit")
|
||||
popup.menu.add("Remove")
|
||||
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.title) {
|
||||
"Edit" -> {
|
||||
val intent = Intent(this, EditCardActivity::class.java)
|
||||
|
||||
intent.putExtra("action", EditCardAction.EDIT.name)
|
||||
intent.putExtra("cardId", card.id)
|
||||
|
||||
startActivity(intent)
|
||||
|
||||
true
|
||||
}
|
||||
"Remove" -> {
|
||||
db.removeCard(card.id)
|
||||
renderCards()
|
||||
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Page(
|
||||
"OmniCards",
|
||||
innerPadding
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()).padding(bottom = 64.dp).background(Color.Transparent).padding(top = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
if (cards.isEmpty()) {
|
||||
Text("Add new card to get started")
|
||||
} else {
|
||||
for (card in cards) {
|
||||
CardButton(card, onDeleteCard, getDbFun)
|
||||
}
|
||||
|
||||
popup.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cardsList.addView(button)
|
||||
if (isActionBarShowed) {
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp).background(MaterialTheme.colorScheme.inversePrimary,
|
||||
RoundedCornerShape(24.dp)).padding(16.dp).fillMaxWidth()
|
||||
) {
|
||||
FullWidthButton(
|
||||
onClick = {
|
||||
isActionBarShowed = false
|
||||
|
||||
val intent = Intent(context, EditCardActivity::class.java)
|
||||
|
||||
context.startActivity(intent)
|
||||
}
|
||||
) {
|
||||
Text("Add card")
|
||||
}
|
||||
FullWidthButton(
|
||||
onClick = {
|
||||
Toast.makeText(context, "Sync in progress...", Toast.LENGTH_SHORT).show()
|
||||
|
||||
sendToWatch(context, "/updateCards", Json.encodeToString(getDbFun().getCards())) { isSuccess ->
|
||||
if (isSuccess) {
|
||||
Toast.makeText(context, "Successfully synced!", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Sync failed! Please, check your connection", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
isActionBarShowed = false
|
||||
}
|
||||
) {
|
||||
Text("Sync with WearOS")
|
||||
}
|
||||
FullWidthButton(
|
||||
onClick = {
|
||||
uriHandler.openUri("https://omni-devel.ru")
|
||||
}
|
||||
) {
|
||||
Text("About author")
|
||||
}
|
||||
FullWidthButton(
|
||||
onClick = {
|
||||
isActionBarShowed = false
|
||||
}
|
||||
) {
|
||||
Text(">")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.BottomEnd).padding(32.dp),
|
||||
onClick = {
|
||||
isActionBarShowed = true
|
||||
}
|
||||
) {
|
||||
Text("<")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user