Compare commits
8
Commits
bdde3d8fbf
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c593e06e4c | ||
|
|
235afe61e7 | ||
|
|
8f79b60399 | ||
|
|
20a62b77b6 | ||
|
|
4748a46156 | ||
|
|
c1a4414f10 | ||
|
|
2a538fdf1c | ||
|
|
ff214eb3d2 |
@@ -23,7 +23,7 @@ android {
|
||||
minSdk = 30
|
||||
targetSdk = 36
|
||||
versionCode = generateVersionCode()
|
||||
versionName = "4.2.1"
|
||||
versionName = "4.3.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -81,4 +81,5 @@ dependencies {
|
||||
implementation("io.ktor:ktor-client-core:3.5.0")
|
||||
implementation("io.ktor:ktor-client-cio:3.5.0")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("sh.calvin.reorderable:reorderable:2.4.3")
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@@ -10,6 +11,14 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.OmniCards">
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:stateNotNeeded="true"
|
||||
android:theme="@style/zxing_CaptureTheme"
|
||||
android:windowSoftInputMode="stateAlwaysHidden"
|
||||
tools:replace="android:screenOrientation" />
|
||||
|
||||
<activity
|
||||
android:name=".SelectIconActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -15,5 +15,6 @@ class CardInfo (
|
||||
val codeType: BarcodeFormat,
|
||||
val codeValue: String,
|
||||
val color: String?,
|
||||
val icon: String?
|
||||
val icon: String?,
|
||||
val position: Int
|
||||
) {}
|
||||
|
||||
@@ -2,15 +2,17 @@ package ru.omni_devel.cards
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import com.google.zxing.BarcodeFormat
|
||||
|
||||
class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?) : SQLiteOpenHelper(context, "omni_cards", factory, 6) {
|
||||
class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?) : SQLiteOpenHelper(context, "omni_cards", factory, 8) {
|
||||
override fun onCreate(db: SQLiteDatabase?) {
|
||||
db!!.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS cards (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, codeType TEXT, codeValue TEXT, color TEXT, icon TEXT)"
|
||||
"CREATE TABLE IF NOT EXISTS cards (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, codeType TEXT, codeValue TEXT, color TEXT, icon TEXT, position DEFAULT 0)"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,6 +44,32 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
if (oldVersion < 6) {
|
||||
db!!.execSQL("ALTER TABLE cards ADD COLUMN icon TEXT")
|
||||
}
|
||||
|
||||
if (oldVersion < 7) {
|
||||
db!!.execSQL("ALTER TABLE cards ADD COLUMN position INTEGER DEFAULT 0")
|
||||
db.execSQL("UPDATE cards SET position = id")
|
||||
}
|
||||
}
|
||||
|
||||
private fun readCard(cursor: Cursor): CardInfo {
|
||||
return CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon")),
|
||||
position = cursor.getInt(cursor.getColumnIndexOrThrow("position"))
|
||||
)
|
||||
}
|
||||
|
||||
private fun nextPosition(db: SQLiteDatabase): Int {
|
||||
val cursor = db.rawQuery("SELECT COALESCE(MAX(position), -1) + 1 FROM cards", null)
|
||||
cursor.moveToFirst()
|
||||
val next = cursor.getInt(0)
|
||||
cursor.close()
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
fun addCard(name: String, codeValue: String, codeType: BarcodeFormat, color: String?, icon: String?) {
|
||||
@@ -54,6 +82,7 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
values.put("codeType", codeType.name)
|
||||
values.put("color", color)
|
||||
values.put("icon", icon)
|
||||
values.put("position", nextPosition(db))
|
||||
|
||||
db.insert("cards", null, values)
|
||||
|
||||
@@ -79,18 +108,11 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
fun getCards(): List<CardInfo> {
|
||||
val db = this.readableDatabase
|
||||
|
||||
val cursor = db.rawQuery("SELECT * FROM cards ORDER BY id ASC", null)
|
||||
val cursor = db.rawQuery("SELECT * FROM cards ORDER BY position ASC", null)
|
||||
val cards = mutableListOf<CardInfo>()
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
cards.add(CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||
))
|
||||
cards.add(readCard(cursor))
|
||||
}
|
||||
|
||||
cursor.close()
|
||||
@@ -111,14 +133,7 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
return null
|
||||
}
|
||||
|
||||
val cardInfo = CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||
)
|
||||
val cardInfo = readCard(cursor)
|
||||
|
||||
cursor.close()
|
||||
db.close()
|
||||
@@ -143,6 +158,8 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
fun addCards(cards: List<CardInfo>) {
|
||||
val db = this.writableDatabase
|
||||
|
||||
db.use { db ->
|
||||
db.transaction {
|
||||
for (card in cards) {
|
||||
val values = ContentValues()
|
||||
|
||||
@@ -152,10 +169,27 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
values.put("codeType", card.codeType.name)
|
||||
values.put("color", card.color)
|
||||
values.put("icon", card.icon)
|
||||
values.put("position", nextPosition(db))
|
||||
|
||||
db.insert("cards", null, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.close()
|
||||
fun updatePositions(orderedIds: List<Int>) {
|
||||
val db = this.writableDatabase
|
||||
|
||||
db.use { db ->
|
||||
db.transaction {
|
||||
orderedIds.forEachIndexed { index, id ->
|
||||
val values = ContentValues()
|
||||
|
||||
values.put("position", index)
|
||||
|
||||
db.update("cards", values, "id = ?", arrayOf(id.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ 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 androidx.core.graphics.toColorInt
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
@@ -96,6 +97,11 @@ fun CardEditable(
|
||||
|
||||
val fieldModifier = Modifier.fillMaxWidth()
|
||||
|
||||
val cardColor = if (color == null)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
Color(color.toColorInt())
|
||||
|
||||
val scanLauncher = rememberLauncherForActivityResult(
|
||||
contract = ScanContract()
|
||||
) { result ->
|
||||
@@ -267,7 +273,7 @@ fun CardEditable(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
CardIcon(if (icon == null) null else base64ToImageBitmap(icon), MaterialTheme.colorScheme.primary)
|
||||
CardIcon(if (icon == null) null else base64ToImageBitmap(icon), cardColor)
|
||||
|
||||
Text(stringResource(R.string.do_select_icon))
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.QrCode
|
||||
import androidx.compose.material.icons.outlined.DensityMedium
|
||||
import androidx.compose.material.icons.outlined.DragHandle
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
@@ -185,7 +186,7 @@ fun TopBar(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
||||
fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit, reorderIcon: (@Composable () -> Unit)? = null) {
|
||||
val context = LocalContext.current
|
||||
|
||||
var menuExpanded by remember { mutableStateOf(false) }
|
||||
@@ -222,6 +223,11 @@ fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
||||
.background(Color.Transparent)
|
||||
.padding(horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
CardIcon(
|
||||
@@ -237,6 +243,11 @@ fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
}
|
||||
|
||||
if (reorderIcon != null) {
|
||||
reorderIcon()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
|
||||
@@ -9,23 +9,26 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.Sync
|
||||
import androidx.compose.material.icons.outlined.Backup
|
||||
import androidx.compose.material.icons.outlined.Code
|
||||
import androidx.compose.material.icons.outlined.DragHandle
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -37,12 +40,15 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var db: DbHelper
|
||||
@@ -60,12 +66,22 @@ class MainActivity : ComponentActivity() {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { innerPadding ->
|
||||
MainPage(innerPadding, cards, onDeleteCard = { cardId ->
|
||||
MainPage(
|
||||
innerPadding,
|
||||
cards,
|
||||
onDeleteCard = { cardId ->
|
||||
db.removeCard(cardId)
|
||||
cards.removeIf { it.id == cardId }
|
||||
}, getDbFun = {
|
||||
syncCardsWithWatch(this, db.getCards())
|
||||
},
|
||||
onReorderCards = {
|
||||
db.updatePositions(it)
|
||||
syncCardsWithWatch(this, db.getCards())
|
||||
},
|
||||
getDbFun = {
|
||||
return@MainPage db
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,14 +92,17 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
cards.clear()
|
||||
cards.addAll(db.getCards())
|
||||
|
||||
syncCardsWithWatch(this, db.getCards())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainPage(
|
||||
innerPadding: PaddingValues,
|
||||
cards: List<CardInfo>,
|
||||
cards: SnapshotStateList<CardInfo>,
|
||||
onDeleteCard: (Int) -> Unit,
|
||||
onReorderCards: (List<Int>) -> Unit,
|
||||
getDbFun: () -> DbHelper
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
@@ -92,6 +111,19 @@ fun MainPage(
|
||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||
var isSearchFieldShowed by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to ->
|
||||
val fromId = from.key as? Int ?: return@rememberReorderableLazyListState
|
||||
val toId = to.key as? Int ?: return@rememberReorderableLazyListState
|
||||
|
||||
val fromIndex = cards.indexOfFirst { it.id == fromId }
|
||||
val toIndex = cards.indexOfFirst { it.id == toId }
|
||||
|
||||
if (fromIndex != -1 && toIndex != -1) {
|
||||
cards.add(toIndex, cards.removeAt(fromIndex))
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
@@ -114,13 +146,14 @@ fun MainPage(
|
||||
NavigationPageData(stringResource(R.string.do_add_card), Icons.Outlined.Add, EditCardActivity::class.java),
|
||||
NavigationPageData(stringResource(R.string.do_sync), Icons.Outlined.Sync) {
|
||||
Toast.makeText(context, context.getString(R.string.sync_in_progress), Toast.LENGTH_SHORT).show()
|
||||
sendToWatch(context, "/updateCards", Json.encodeToString(getDbFun().getCards())) { isSuccess ->
|
||||
|
||||
val isSuccess = syncCardsWithWatch(context, getDbFun().getCards())
|
||||
|
||||
if (isSuccess) {
|
||||
Toast.makeText(context, context.getString(R.string.sync_is_successful), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, context.getString(R.string.sync_is_fail), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
NavigationPageData(stringResource(R.string.open_backup_menu), Icons.Outlined.Backup, BackupActivity::class.java),
|
||||
NavigationPageData(stringResource(R.string.source_code), Icons.Outlined.Code) {
|
||||
@@ -147,14 +180,11 @@ fun MainPage(
|
||||
cardNameCleaned = cardNameCleaned.replace(from, to)
|
||||
}
|
||||
|
||||
if (cardNameCleaned.contains(queryCleaned)) {
|
||||
return@filter true
|
||||
}
|
||||
|
||||
return@filter false
|
||||
return@filter cardNameCleaned.contains(queryCleaned)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
item {
|
||||
@@ -188,8 +218,40 @@ fun MainPage(
|
||||
}
|
||||
} else {
|
||||
for (card in searchResults) {
|
||||
item {
|
||||
CardButton(card, onDeleteCard)
|
||||
item(key = card.id) {
|
||||
ReorderableItem(reorderableLazyListState, key = card.id) { isDragging ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(if (isDragging) 0.7f else 1f)
|
||||
) {
|
||||
var reorderIcon: (@Composable () -> Unit)? = null
|
||||
|
||||
if (queryCleaned.isEmpty()) {
|
||||
reorderIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.DragHandle,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier
|
||||
.draggableHandle(
|
||||
onDragStopped = {
|
||||
onReorderCards(cards.map { it.id })
|
||||
}
|
||||
)
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CardButton(
|
||||
card,
|
||||
onDeleteCard,
|
||||
reorderIcon
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayOutputStream
|
||||
import android.util.Base64
|
||||
import android.widget.Toast
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
||||
val writer = MultiFormatWriter()
|
||||
@@ -33,12 +35,14 @@ fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
||||
return BarcodeEncoder().createBitmap(matrix)
|
||||
}
|
||||
|
||||
fun sendToWatch(context: Context, path: String, message: String, onResult: (Boolean) -> Unit) {
|
||||
fun sendToWatch(context: Context, path: String, message: String): Boolean {
|
||||
val nodeClient = Wearable.getNodeClient(context)
|
||||
|
||||
var isSuccess = false
|
||||
|
||||
nodeClient.connectedNodes.addOnSuccessListener { nodes ->
|
||||
if (nodes.isEmpty()) {
|
||||
onResult(false)
|
||||
isSuccess = false
|
||||
|
||||
return@addOnSuccessListener
|
||||
}
|
||||
@@ -59,13 +63,19 @@ fun sendToWatch(context: Context, path: String, message: String, onResult: (Bool
|
||||
}
|
||||
|
||||
if (completedCount == nodes.size) {
|
||||
onResult(isAnySuccess)
|
||||
isSuccess = isAnySuccess
|
||||
}
|
||||
}.addOnFailureListener(context.mainExecutor) {
|
||||
onResult(false)
|
||||
isSuccess = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isSuccess
|
||||
}
|
||||
|
||||
fun syncCardsWithWatch(context: Context, cards: List<CardInfo>): Boolean {
|
||||
return sendToWatch(context, "/updateCards", Json.encodeToString(cards))
|
||||
}
|
||||
|
||||
fun checkCardData(name: String, value: String): Int? {
|
||||
|
||||
@@ -23,7 +23,7 @@ android {
|
||||
minSdk = 30
|
||||
targetSdk = 36
|
||||
versionCode = generateVersionCode()
|
||||
versionName = "3.2.0"
|
||||
versionName = "3.3.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -72,4 +72,5 @@ dependencies {
|
||||
implementation(libs.material)
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.lifecycle:lifecycle-process:2.8.7")
|
||||
}
|
||||
@@ -10,5 +10,6 @@ class CardInfo (
|
||||
val codeType: BarcodeFormat,
|
||||
val codeValue: String,
|
||||
val color: String?,
|
||||
val icon: String?
|
||||
val icon: String?,
|
||||
val position: Int
|
||||
)
|
||||
@@ -2,12 +2,14 @@ package ru.omni_devel.cards.presentation
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import com.google.zxing.BarcodeFormat
|
||||
|
||||
class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?) : SQLiteOpenHelper(context, "omni_cards", factory, 5) {
|
||||
class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?) : SQLiteOpenHelper(context, "omni_cards", factory, 6) {
|
||||
override fun onCreate(db: SQLiteDatabase?) {
|
||||
db!!.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS cards (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, codeType TEXT, codeValue TEXT, color TEXT, icon TEXT)"
|
||||
@@ -37,6 +39,23 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
if (oldVersion < 5) {
|
||||
db!!.execSQL("ALTER TABLE cards ADD COLUMN icon TEXT")
|
||||
}
|
||||
|
||||
if (oldVersion < 6) {
|
||||
db!!.execSQL("ALTER TABLE cards ADD COLUMN position INTEGER DEFAULT 0")
|
||||
db.execSQL("UPDATE cards SET position = id")
|
||||
}
|
||||
}
|
||||
|
||||
private fun readCard(cursor: Cursor): CardInfo {
|
||||
return CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon")),
|
||||
position = cursor.getInt(cursor.getColumnIndexOrThrow("position"))
|
||||
)
|
||||
}
|
||||
|
||||
fun clearDatabase() {
|
||||
@@ -50,6 +69,8 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
fun addCards(cards: List<CardInfo>) {
|
||||
val db = this.writableDatabase
|
||||
|
||||
db.use { db ->
|
||||
db.transaction {
|
||||
for (card in cards) {
|
||||
val values = ContentValues()
|
||||
|
||||
@@ -59,28 +80,22 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
values.put("codeType", card.codeType.name)
|
||||
values.put("color", card.color)
|
||||
values.put("icon", card.icon)
|
||||
values.put("position", card.position)
|
||||
|
||||
db.insert("cards", null, values)
|
||||
}
|
||||
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCards(): List<CardInfo> {
|
||||
val db = this.readableDatabase
|
||||
|
||||
val cursor = db.rawQuery("SELECT * FROM cards", null)
|
||||
val cursor = db.rawQuery("SELECT * FROM cards ORDER BY position", null)
|
||||
val cards = mutableListOf<CardInfo>()
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
cards.add(CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||
))
|
||||
cards.add(readCard(cursor))
|
||||
}
|
||||
|
||||
cursor.close()
|
||||
@@ -101,14 +116,7 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
||||
return null
|
||||
}
|
||||
|
||||
val cardInfo = CardInfo(
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow("id")),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||
)
|
||||
val cardInfo = readCard(cursor)
|
||||
|
||||
cursor.close()
|
||||
db.close()
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -22,16 +23,25 @@ import ru.omni_devel.cards.R
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var db: DbHelper
|
||||
private val cardsState = mutableStateOf<List<CardInfo>>(emptyList())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
db = DbHelper(this, null)
|
||||
|
||||
cardsState.value = db.getCards()
|
||||
|
||||
setContent {
|
||||
App(db.getCards())
|
||||
App(cardsState.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
cardsState.value = db.getCards()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.omni_devel.cards.R
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.Lifecycle
|
||||
|
||||
class MessagesReceiver : WearableListenerService() {
|
||||
private lateinit var db: DbHelper
|
||||
@@ -24,12 +26,26 @@ class MessagesReceiver : WearableListenerService() {
|
||||
val json = String(event.data, Charsets.UTF_8)
|
||||
|
||||
val cards = Json.decodeFromString<List<CardInfo>>(json)
|
||||
val oldCards = db.getCards()
|
||||
|
||||
if (cards == oldCards) {
|
||||
return
|
||||
}
|
||||
|
||||
db.clearDatabase()
|
||||
db.addCards(cards)
|
||||
|
||||
val isAppInForeground = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(
|
||||
Lifecycle.State.STARTED
|
||||
)
|
||||
|
||||
if (isAppInForeground) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
Toast.makeText(this, this.getString(R.string.sync_is_successful), Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
this,
|
||||
this.getString(R.string.sync_is_successful),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
@@ -39,4 +55,5 @@ class MessagesReceiver : WearableListenerService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user