forked from omni/OmniCards
refactor: rewrite mobile app in Compose and add error handling for wear
This commit is contained in:
@@ -19,6 +19,9 @@ activityCompose = "1.13.0"
|
|||||||
coreSplashscreen = "1.2.0"
|
coreSplashscreen = "1.2.0"
|
||||||
zxing = "3.5.3"
|
zxing = "3.5.3"
|
||||||
zxingAndroid = "4.3.0"
|
zxingAndroid = "4.3.0"
|
||||||
|
lifecycleRuntimeKtx = "2.6.1"
|
||||||
|
navigationFragmentKtx = "2.6.0"
|
||||||
|
navigationUiKtx = "2.6.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -45,6 +48,10 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp
|
|||||||
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" }
|
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" }
|
||||||
zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing" }
|
zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing" }
|
||||||
zxing-android = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxingAndroid" }
|
zxing-android = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxingAndroid" }
|
||||||
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
|
||||||
|
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
+20
-3
@@ -1,5 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
kotlin("plugin.serialization") version "2.2.20"
|
kotlin("plugin.serialization") version "2.2.20"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ android {
|
|||||||
applicationId = "ru.omni_devel.cards"
|
applicationId = "ru.omni_devel.cards"
|
||||||
minSdk = 30
|
minSdk = 30
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 2
|
||||||
versionName = "1.0"
|
versionName = "2.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -34,18 +35,34 @@ android {
|
|||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.activity.ktx)
|
implementation(libs.androidx.activity.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
|
implementation(libs.androidx.compose.ui)
|
||||||
|
implementation(libs.androidx.compose.ui.graphics)
|
||||||
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.constraintlayout)
|
implementation(libs.androidx.constraintlayout)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
implementation(libs.androidx.material3)
|
||||||
|
implementation(libs.androidx.navigation.fragment.ktx)
|
||||||
|
implementation(libs.androidx.navigation.ui.ktx)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
implementation(libs.play.services.wearable)
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
implementation(libs.zxing.core)
|
implementation(libs.zxing.core)
|
||||||
implementation(libs.zxing.android)
|
implementation(libs.zxing.android)
|
||||||
implementation("com.google.android.gms:play-services-wearable:18.1.0")
|
implementation("com.google.android.gms:play-services-wearable:18.1.0")
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,28 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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">
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Base.Theme.OmniCards">
|
android:theme="@style/Theme.OmniCards">
|
||||||
<activity
|
|
||||||
android:name=".ShowCardActivity"
|
|
||||||
android:screenOrientation="portrait"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".EditCardActivity"
|
android:name=".EditCardActivity"
|
||||||
android:screenOrientation="portrait"
|
android:exported="false"
|
||||||
android:exported="false" />
|
android:label="@string/title_activity_edit_card"
|
||||||
|
android:theme="@style/Theme.OmniCards" />
|
||||||
|
<activity
|
||||||
|
android:name=".AddCardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/title_activity_add_card"
|
||||||
|
android:theme="@style/Theme.OmniCards" />
|
||||||
|
<activity
|
||||||
|
android:name=".ShowCardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/title_activity_show_card"
|
||||||
|
android:theme="@style/Theme.OmniCards" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:screenOrientation="portrait"
|
android:exported="true"
|
||||||
android:exported="true">
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/Theme.OmniCards">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +1,249 @@
|
|||||||
package ru.omni_devel.cards
|
package ru.omni_devel.cards
|
||||||
|
|
||||||
import android.content.Intent
|
import android.app.Activity
|
||||||
import android.os.Bundle
|
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 android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.core.view.WindowInsetsCompat
|
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.google.zxing.BarcodeFormat
|
||||||
import com.journeyapps.barcodescanner.ScanContract
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
import com.journeyapps.barcodescanner.ScanOptions
|
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 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
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)
|
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 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)
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (action) {
|
|
||||||
EditCardAction.ADD -> {
|
|
||||||
setCodeType("QR_CODE")
|
|
||||||
|
|
||||||
actionLabel.text = "Adding card"
|
|
||||||
}
|
|
||||||
EditCardAction.EDIT -> {
|
|
||||||
val cardId = intent.getIntExtra("cardId", -1)
|
|
||||||
|
|
||||||
val cardInfo = db.getCard(cardId)
|
val cardInfo = db.getCard(cardId)
|
||||||
|
|
||||||
if (cardInfo == null) {
|
setContent {
|
||||||
Toast.makeText(this, "cardInfo is null", Toast.LENGTH_LONG).show()
|
OmniCardsTheme {
|
||||||
return
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
}
|
EditCardPage(cardInfo, innerPadding, getDbFun = {
|
||||||
|
return@EditCardPage db
|
||||||
actionLabel.text = "Editing card"
|
})
|
||||||
|
|
||||||
cardNameInput.setText(cardInfo.name)
|
|
||||||
cardValueInput.setText(cardInfo.codeValue)
|
|
||||||
setCodeType(cardInfo.codeType.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardEditable(
|
||||||
|
cardName: String,
|
||||||
|
onCardNameChange: (String) -> Unit,
|
||||||
|
codeValue: String,
|
||||||
|
oncodeValueChange: (String) -> Unit,
|
||||||
|
codeType: BarcodeFormat,
|
||||||
|
onCodeTypeChange: (BarcodeFormat) -> Unit
|
||||||
|
) {
|
||||||
|
var isCodeTypeDropdownExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
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 (e: IllegalArgumentException) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setCodeType(codeTypeString: String) {
|
@Composable
|
||||||
val codeTypes = BarcodeFormat.entries.map { it.name }
|
fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
||||||
val codeTypeIndex = codeTypes.indexOf(codeTypeString)
|
var cardName by remember { mutableStateOf("") }
|
||||||
|
var codeValue by remember { mutableStateOf("") }
|
||||||
|
var codeType by remember { mutableStateOf(BarcodeFormat.QR_CODE) }
|
||||||
|
|
||||||
if (codeTypeIndex >= 0) {
|
val context = LocalContext.current as Activity
|
||||||
findViewById<Spinner>(R.id.cardCodeTypeSelector).setSelection(codeTypeIndex)
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package ru.omni_devel.cards
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Page(name: String, innerPadding: PaddingValues, content: @Composable () -> Unit) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
TopBar(name)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TopBar(text: String) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.primaryContainer).padding(horizontal = 12.dp, vertical = 16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit, getDbFun: () -> DbHelper) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
var menuExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onTap = {
|
||||||
|
val intent = Intent(context, ShowCardActivity::class.java)
|
||||||
|
intent.putExtra("cardId", cardInfo.id)
|
||||||
|
context.startActivity(intent)
|
||||||
|
},
|
||||||
|
onLongPress = {
|
||||||
|
menuExpanded = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
shape = ButtonDefaults.shape,
|
||||||
|
color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
tonalElevation = 2.dp
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
cardInfo.name,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 16.dp, horizontal = 16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
textAlign = TextAlign.Start
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = menuExpanded,
|
||||||
|
onDismissRequest = { menuExpanded = false }
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Edit") },
|
||||||
|
onClick = {
|
||||||
|
menuExpanded = false
|
||||||
|
|
||||||
|
val intent = Intent(context, EditCardActivity::class.java)
|
||||||
|
|
||||||
|
intent.putExtra("cardId", cardInfo.id)
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Delete") },
|
||||||
|
onClick = {
|
||||||
|
menuExpanded = false
|
||||||
|
|
||||||
|
onDeleteCard(cardInfo.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FullWidthButton(onClick: () -> Unit, content: @Composable () -> Unit) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onClick = onClick
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,55 +2,64 @@ package ru.omni_devel.cards
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
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 android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.compose.foundation.background
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.compose.foundation.layout.Box
|
||||||
import com.google.android.material.button.MaterialButton
|
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 kotlinx.serialization.json.Json
|
||||||
|
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private lateinit var cardsList: LinearLayout
|
|
||||||
private lateinit var db: DbHelper
|
private lateinit var db: DbHelper
|
||||||
|
|
||||||
|
private val cards = mutableStateListOf<CardInfo>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
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)
|
db = DbHelper(this, null)
|
||||||
cardsList = findViewById(R.id.cardsList)
|
|
||||||
|
|
||||||
val addCardButton: Button = findViewById(R.id.addCardButton)
|
setContent {
|
||||||
val syncWithWearOSButton: Button = findViewById(R.id.syncWithWearOSButton)
|
OmniCardsTheme {
|
||||||
|
Scaffold(
|
||||||
addCardButton.setOnClickListener {
|
modifier = Modifier.fillMaxSize()
|
||||||
val intent = Intent(this, EditCardActivity::class.java)
|
) { innerPadding ->
|
||||||
|
MainPage(innerPadding, cards, onDeleteCard = { cardId ->
|
||||||
intent.putExtra("action", EditCardAction.ADD.name)
|
db.removeCard(cardId)
|
||||||
|
cards.removeIf { it.id == cardId }
|
||||||
startActivity(intent)
|
}, getDbFun = {
|
||||||
}
|
return@MainPage db
|
||||||
|
})
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,83 +67,101 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
renderCards()
|
|
||||||
|
cards.clear()
|
||||||
|
cards.addAll(db.getCards())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderCards() {
|
@Composable
|
||||||
val cards = db.getCards()
|
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) }
|
||||||
|
|
||||||
|
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()) {
|
if (cards.isEmpty()) {
|
||||||
val text = TextView(this)
|
Text("Add new card to get started")
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
for (card in cards) {
|
for (card in cards) {
|
||||||
val button = MaterialButton(this)
|
CardButton(card, onDeleteCard, getDbFun)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
popup.show()
|
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
|
||||||
|
|
||||||
true
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cardsList.addView(button)
|
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("<")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,35 +2,61 @@ package ru.omni_devel.cards
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.compose.foundation.Image
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||||
|
|
||||||
class ShowCardActivity : AppCompatActivity() {
|
class ShowCardActivity : ComponentActivity() {
|
||||||
private lateinit var db: DbHelper
|
private lateinit var db: DbHelper
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContentView(R.layout.activity_show_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
|
|
||||||
}
|
|
||||||
|
|
||||||
db = DbHelper(this, null)
|
db = DbHelper(this, null)
|
||||||
|
|
||||||
|
val cardId = intent.getIntExtra("cardId", -1)
|
||||||
|
|
||||||
|
val cardInfo = db.getCard(cardId)
|
||||||
|
|
||||||
|
if (cardInfo == null) {
|
||||||
|
Toast.makeText(this, "Card is not exists", Toast.LENGTH_LONG).show()
|
||||||
|
finish()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
OmniCardsTheme {
|
||||||
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
|
ShowCardPage(innerPadding, cardInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
setBrightness(1.0f)
|
setBrightness(1f)
|
||||||
renderCard()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@@ -39,31 +65,46 @@ class ShowCardActivity : AppCompatActivity() {
|
|||||||
setBrightness(WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE)
|
setBrightness(WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderCard() {
|
|
||||||
val cardId = intent.getIntExtra("cardId", 0)
|
|
||||||
|
|
||||||
val cardInfo = db.getCard(cardId)
|
|
||||||
|
|
||||||
if (cardInfo == null) {
|
|
||||||
Toast.makeText(this, "cardInfo is null", Toast.LENGTH_LONG).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val cardCodeView: ImageView = findViewById(R.id.cardCodeView)
|
|
||||||
val cardNameElement: TextView = findViewById(R.id.editCardActionLabel)
|
|
||||||
|
|
||||||
cardNameElement.text = cardInfo.name
|
|
||||||
|
|
||||||
val code = generateCode(cardInfo.codeValue, cardInfo.codeType)
|
|
||||||
|
|
||||||
cardCodeView.setImageBitmap(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setBrightness(level: Float) {
|
private fun setBrightness(level: Float) {
|
||||||
val params = window.attributes
|
window.attributes.screenBrightness = level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
params.screenBrightness = level
|
@Composable
|
||||||
|
fun ShowCardPage(innerPadding: PaddingValues, cardInfo: CardInfo) {
|
||||||
|
val bitmap = remember(cardInfo) {
|
||||||
|
try {
|
||||||
|
generateCode(cardInfo.codeValue, cardInfo.codeType).asImageBitmap()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.attributes = params
|
Page(cardInfo.name, innerPadding) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (bitmap != null) {
|
||||||
|
Image(
|
||||||
|
bitmap = bitmap,
|
||||||
|
contentDescription = cardInfo.codeValue,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentScale = ContentScale.FillWidth
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
cardInfo.codeValue,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
"Failed to generate code. Check card data",
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package ru.omni_devel.cards
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.widget.Toast
|
|
||||||
import com.google.android.gms.wearable.Wearable
|
import com.google.android.gms.wearable.Wearable
|
||||||
import com.google.zxing.BarcodeFormat
|
import com.google.zxing.BarcodeFormat
|
||||||
import com.google.zxing.MultiFormatWriter
|
import com.google.zxing.MultiFormatWriter
|
||||||
@@ -10,16 +9,14 @@ import com.journeyapps.barcodescanner.BarcodeEncoder
|
|||||||
|
|
||||||
fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
||||||
val writer = MultiFormatWriter()
|
val writer = MultiFormatWriter()
|
||||||
val matrix = writer.encode(value, format, 600, 300)
|
val matrix = writer.encode(value, format, 800, 400)
|
||||||
|
|
||||||
return BarcodeEncoder().createBitmap(matrix)
|
return BarcodeEncoder().createBitmap(matrix)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendToWatch(context: Context, path: String, message: String, onResult: (Boolean) -> Unit): Boolean {
|
fun sendToWatch(context: Context, path: String, message: String, onResult: (Boolean) -> Unit) {
|
||||||
val nodeClient = Wearable.getNodeClient(context)
|
val nodeClient = Wearable.getNodeClient(context)
|
||||||
|
|
||||||
var isSuccess = false
|
|
||||||
|
|
||||||
nodeClient.connectedNodes.addOnSuccessListener { nodes ->
|
nodeClient.connectedNodes.addOnSuccessListener { nodes ->
|
||||||
if (nodes.isEmpty()) {
|
if (nodes.isEmpty()) {
|
||||||
onResult(false)
|
onResult(false)
|
||||||
@@ -35,7 +32,7 @@ fun sendToWatch(context: Context, path: String, message: String, onResult: (Bool
|
|||||||
node.id,
|
node.id,
|
||||||
path,
|
path,
|
||||||
message.toByteArray(Charsets.UTF_8)
|
message.toByteArray(Charsets.UTF_8)
|
||||||
).addOnCompleteListener { task ->
|
).addOnCompleteListener(context.mainExecutor) { task ->
|
||||||
completedCount++
|
completedCount++
|
||||||
|
|
||||||
if (task.isSuccessful) {
|
if (task.isSuccessful) {
|
||||||
@@ -45,11 +42,19 @@ fun sendToWatch(context: Context, path: String, message: String, onResult: (Bool
|
|||||||
if (completedCount == nodes.size) {
|
if (completedCount == nodes.size) {
|
||||||
onResult(isAnySuccess)
|
onResult(isAnySuccess)
|
||||||
}
|
}
|
||||||
}.addOnFailureListener {
|
}.addOnFailureListener(context.mainExecutor) {
|
||||||
onResult(false)
|
onResult(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return isSuccess
|
fun checkCardData(name: String, value: String): String? {
|
||||||
|
if (name.trim().isEmpty()) {
|
||||||
|
return "Name should not be empty"
|
||||||
|
} else if (value.trim().isEmpty()) {
|
||||||
|
return "Code value should not be empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ru.omni_devel.cards.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
val Purple80 = Color(0xFFD0BCFF)
|
||||||
|
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||||
|
val Pink80 = Color(0xFFEFB8C8)
|
||||||
|
|
||||||
|
val Purple40 = Color(0xFF6650a4)
|
||||||
|
val PurpleGrey40 = Color(0xFF625b71)
|
||||||
|
val Pink40 = Color(0xFF7D5260)
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package ru.omni_devel.cards.ui.theme
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
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 DarkColorScheme = darkColorScheme(
|
||||||
|
primary = Purple80,
|
||||||
|
secondary = PurpleGrey80,
|
||||||
|
tertiary = Pink80
|
||||||
|
)
|
||||||
|
|
||||||
|
private val LightColorScheme = lightColorScheme(
|
||||||
|
primary = Purple40,
|
||||||
|
secondary = PurpleGrey40,
|
||||||
|
tertiary = Pink40
|
||||||
|
|
||||||
|
/* Other default colors to override
|
||||||
|
background = Color(0xFFFFFBFE),
|
||||||
|
surface = Color(0xFFFFFBFE),
|
||||||
|
onPrimary = Color.White,
|
||||||
|
onSecondary = Color.White,
|
||||||
|
onTertiary = Color.White,
|
||||||
|
onBackground = Color(0xFF1C1B1F),
|
||||||
|
onSurface = Color(0xFF1C1B1F),
|
||||||
|
*/
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun OmniCardsTheme(
|
||||||
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
// Dynamic color is available on Android 12+
|
||||||
|
dynamicColor: Boolean = true,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val colorScheme = when {
|
||||||
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
darkTheme -> DarkColorScheme
|
||||||
|
else -> LightColorScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package ru.omni_devel.cards.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
// Set of Material typography styles to start with
|
||||||
|
val Typography = Typography(
|
||||||
|
bodyLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 24.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
/* Other default text styles to override
|
||||||
|
titleLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
|
),
|
||||||
|
labelSmall = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
)
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".EditCardActivity">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar2"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
android:minHeight="?attr/actionBarSize"
|
|
||||||
android:theme="?attr/actionBarTheme"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/editCardActionLabel"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:text="Action"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="24sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar2"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/toolbar2"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/toolbar2" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar2"
|
|
||||||
app:layout_constraintVertical_bias="0.0">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cardNameInput"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="46dp"
|
|
||||||
android:ems="10"
|
|
||||||
android:hint="Card name"
|
|
||||||
android:inputType="text" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/cardValueInput"
|
|
||||||
android:layout_width="259dp"
|
|
||||||
android:layout_height="46dp"
|
|
||||||
android:ems="10"
|
|
||||||
android:hint="Card value"
|
|
||||||
android:inputType="text" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/scanCodeButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="38dp"
|
|
||||||
android:text="Scan" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/cardCodeTypeSelector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/saveCardButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Save" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".MainActivity">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
android:minHeight="?attr/actionBarSize"
|
|
||||||
android:theme="?attr/actionBarTheme"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:text="OmniCards"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="24sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/toolbar"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/toolbar" />
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/cardsList"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/addCardButton"
|
|
||||||
android:layout_width="64dp"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:text="+"
|
|
||||||
android:textSize="24sp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/syncWithWearOSButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:backgroundTint="#0E000000"
|
|
||||||
android:text="Sync with WearOS"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/toolbar"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/toolbar" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,43 +1,36 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/main"
|
android:id="@+id/main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".EditCardActivity">
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context=".ShowCardActivity">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/toolbar2"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorPrimary"
|
android:layout_width="match_parent"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:fitsSystemWindows="true"
|
||||||
android:theme="?attr/actionBarTheme"
|
>
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/editCardActionLabel"
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width = "match_parent"
|
||||||
|
android:layout_height = "?attr/actionBarSize" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/content_show_card"/>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_gravity="bottom|end"
|
||||||
android:text="Card name"
|
android:layout_marginEnd="@dimen/fab_margin"
|
||||||
android:textColor="@color/text_primary"
|
android:layout_marginBottom="16dp"
|
||||||
android:textSize="24sp"
|
app:srcCompat="@android:drawable/ic_dialog_email" />
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar2"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/toolbar2"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/toolbar2" />
|
|
||||||
|
|
||||||
<ImageView
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
android:id="@+id/cardCodeView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="300dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar2" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host_fragment_content_show_card"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/nav_graph" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".FirstFragment">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_first"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/next"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textview_first"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textview_first"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/lorem_ipsum"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/button_first" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".SecondFragment">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_second"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/previous"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textview_second"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textview_second"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/lorem_ipsum"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/button_second" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -1,18 +1,4 @@
|
|||||||
<!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
|||||||
@@ -1,18 +1,4 @@
|
|||||||
<!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/nav_graph"
|
||||||
|
app:startDestination="@id/FirstFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/FirstFragment"
|
||||||
|
android:name="ru.omni_devel.cards.FirstFragment"
|
||||||
|
android:label="@string/first_fragment_label"
|
||||||
|
tools:layout="@layout/fragment_first" >
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_FirstFragment_to_SecondFragment"
|
||||||
|
app:destination="@id/SecondFragment" />
|
||||||
|
</fragment>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/SecondFragment"
|
||||||
|
android:name="ru.omni_devel.cards.SecondFragment"
|
||||||
|
android:label="@string/second_fragment_label"
|
||||||
|
tools:layout="@layout/fragment_second" >
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_SecondFragment_to_FirstFragment"
|
||||||
|
app:destination="@id/FirstFragment" />
|
||||||
|
</fragment>
|
||||||
|
</navigation>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<dimen name="fab_margin">48dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.OmniCards" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your dark theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<style name="Theme.OmniCards" parent="Base.Theme.OmniCards">
|
||||||
|
<!-- Transparent system bars for edge-to-edge. -->
|
||||||
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<dimen name="fab_margin">200dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<dimen name="fab_margin">48dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -1,33 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Backgrounds -->
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
<color name="bg_app">#0B0E17</color>
|
<color name="purple_500">#FF6200EE</color>
|
||||||
<color name="bg_surface">#0E1118</color>
|
<color name="purple_700">#FF3700B3</color>
|
||||||
<color name="bg_card">#131726</color>
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
<color name="bg_elevated">#1A2235</color>
|
<color name="teal_700">#FF018786</color>
|
||||||
<color name="bg_hover">#212B42</color>
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
<!-- Accent (indigo) -->
|
|
||||||
<color name="accent_subtle">#152045</color>
|
|
||||||
<color name="accent_primary">#2952C8</color>
|
|
||||||
<color name="accent_hover">#3D6AE8</color>
|
|
||||||
<color name="accent_light">#6B90FF</color>
|
|
||||||
|
|
||||||
<!-- Text -->
|
|
||||||
<color name="text_primary">#DCE3F5</color>
|
|
||||||
<color name="text_secondary">#8A9BBC</color>
|
|
||||||
<color name="text_hint">#4E5E80</color>
|
|
||||||
|
|
||||||
<!-- Borders -->
|
|
||||||
<color name="border_subtle">#1C2030</color>
|
|
||||||
<color name="border_default">#1C2A42</color>
|
|
||||||
<color name="border_active">#2A3D60</color>
|
|
||||||
|
|
||||||
<!-- Semantic -->
|
|
||||||
<color name="success_bg">#0F3320</color>
|
|
||||||
<color name="success">#2ECC71</color>
|
|
||||||
<color name="error_bg">#3D1515</color>
|
|
||||||
<color name="error">#E05555</color>
|
|
||||||
<color name="warning_bg">#3D2E00</color>
|
|
||||||
<color name="warning">#F0A500</color>
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -1,3 +1,48 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">OmniCards</string>
|
<string name="app_name">OmniCards</string>
|
||||||
|
<!-- Strings used for fragments for navigation -->
|
||||||
|
<string name="first_fragment_label">First Fragment</string>
|
||||||
|
<string name="second_fragment_label">Second Fragment</string>
|
||||||
|
<string name="next">Next</string>
|
||||||
|
<string name="previous">Previous</string>
|
||||||
|
|
||||||
|
<string name="lorem_ipsum">
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris
|
||||||
|
volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus
|
||||||
|
dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad
|
||||||
|
litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend
|
||||||
|
diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a,
|
||||||
|
ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
|
||||||
|
Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus
|
||||||
|
egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed
|
||||||
|
neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada
|
||||||
|
fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae,
|
||||||
|
molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor
|
||||||
|
bibendum, vel congue leo egestas.\n\n
|
||||||
|
Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit
|
||||||
|
amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel,
|
||||||
|
molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer
|
||||||
|
interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at
|
||||||
|
lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula,
|
||||||
|
in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque
|
||||||
|
est.\n\n
|
||||||
|
Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh.
|
||||||
|
Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui
|
||||||
|
non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In
|
||||||
|
eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc,
|
||||||
|
quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra
|
||||||
|
ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a
|
||||||
|
placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus
|
||||||
|
convallis.\n\n
|
||||||
|
Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et
|
||||||
|
malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa
|
||||||
|
gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper,
|
||||||
|
libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper
|
||||||
|
sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus
|
||||||
|
libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus
|
||||||
|
vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
|
||||||
|
</string>
|
||||||
|
<string name="title_activity_show_card">ShowCardActivity</string>
|
||||||
|
<string name="title_activity_add_card">AddCardActivity</string>
|
||||||
|
<string name="title_activity_edit_card">EditCardActivity</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<style name="CardButton" parent="Widget.Material3.Button">
|
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">64dp</item>
|
|
||||||
<item name="android:textAllCaps">false</item>
|
|
||||||
<item name="android:textSize">16sp</item>
|
|
||||||
<item name="cornerRadius">8dp</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
@@ -1,29 +1,10 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Theme.OmniCards" parent="android:Theme.Material.Light.NoActionBar" />
|
||||||
|
<!-- Base application theme. -->
|
||||||
<style name="Base.Theme.OmniCards" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="Base.Theme.OmniCards" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<item name="colorPrimary">@color/accent_primary</item>
|
<!-- Customize your light theme here. -->
|
||||||
<item name="colorOnPrimary">@color/text_primary</item>
|
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||||
<item name="colorPrimaryContainer">@color/accent_subtle</item>
|
|
||||||
<item name="colorOnPrimaryContainer">@color/accent_light</item>
|
|
||||||
|
|
||||||
<item name="colorSurface">@color/bg_card</item>
|
|
||||||
<item name="colorOnSurface">@color/text_primary</item>
|
|
||||||
<item name="colorSurfaceVariant">@color/bg_elevated</item>
|
|
||||||
<item name="colorOnSurfaceVariant">@color/text_secondary</item>
|
|
||||||
|
|
||||||
<item name="colorOutline">@color/border_default</item>
|
|
||||||
<item name="colorOutlineVariant">@color/border_subtle</item>
|
|
||||||
|
|
||||||
<item name="colorError">@color/error</item>
|
|
||||||
<item name="colorOnError">@color/text_primary</item>
|
|
||||||
<item name="colorErrorContainer">@color/error_bg</item>
|
|
||||||
|
|
||||||
<item name="android:windowBackground">@color/bg_app</item>
|
|
||||||
<item name="android:textColorPrimary">@color/text_primary</item>
|
|
||||||
<item name="android:textColorSecondary">@color/text_secondary</item>
|
|
||||||
<item name="android:textColorHint">@color/text_hint</item>
|
|
||||||
<item name="android:colorBackground">@color/bg_app</item>
|
|
||||||
|
|
||||||
<item name="android:statusBarColor">@color/bg_app</item>
|
|
||||||
<item name="android:navigationBarColor">@color/bg_app</item>
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample backup rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/guide/topics/data/autobackup
|
|
||||||
for details.
|
|
||||||
Note: This file is ignored for devices older than API 31
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore
|
|
||||||
-->
|
|
||||||
<full-backup-content>
|
|
||||||
<!--
|
|
||||||
<include domain="sharedpref" path="."/>
|
|
||||||
<exclude domain="sharedpref" path="device.xml"/>
|
|
||||||
-->
|
|
||||||
</full-backup-content>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample data extraction rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
|
||||||
for details.
|
|
||||||
-->
|
|
||||||
<data-extraction-rules>
|
|
||||||
<cloud-backup>
|
|
||||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
-->
|
|
||||||
</cloud-backup>
|
|
||||||
<!--
|
|
||||||
<device-transfer>
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
</device-transfer>
|
|
||||||
-->
|
|
||||||
</data-extraction-rules>
|
|
||||||
+1
-2
@@ -23,6 +23,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "OmniCards"
|
rootProject.name = "OmniCards"
|
||||||
include(":mobile")
|
|
||||||
include(":wear")
|
include(":wear")
|
||||||
|
include(":mobile")
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ android {
|
|||||||
applicationId = "ru.omni_devel.cards"
|
applicationId = "ru.omni_devel.cards"
|
||||||
minSdk = 30
|
minSdk = 30
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 2
|
||||||
versionName = "1.0"
|
versionName = "1.1"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -12,11 +12,21 @@ import androidx.compose.foundation.layout.Arrangement
|
|||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.wear.compose.material3.MaterialTheme
|
||||||
|
import androidx.wear.compose.material3.Text
|
||||||
|
|
||||||
|
const val CODE_WIDTH = 380
|
||||||
|
const val CODE_HEIGHT = 380
|
||||||
|
|
||||||
class ShowCardActivity : ComponentActivity() {
|
class ShowCardActivity : ComponentActivity() {
|
||||||
private lateinit var db: DbHelper
|
private lateinit var db: DbHelper
|
||||||
@@ -43,7 +53,7 @@ class ShowCardActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
CardView(cardInfo)
|
ShowCardPage(cardInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,25 +79,40 @@ class ShowCardActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CardView(cardInfo: CardInfo) {
|
fun ShowCardPage(cardInfo: CardInfo) {
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
) {
|
|
||||||
BoxWithConstraints(
|
BoxWithConstraints(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val screenSize = minOf(maxWidth, maxHeight)
|
||||||
|
val safeSize = (screenSize * 0.65f)
|
||||||
|
val sizePx = with(LocalDensity.current) { safeSize.roundToPx() }
|
||||||
|
|
||||||
val widthPx = with(density) { maxWidth.roundToPx() }
|
val bitmap = remember(cardInfo, sizePx) {
|
||||||
val heightPx = with(density) { maxHeight.roundToPx() }
|
try {
|
||||||
|
generateCode(cardInfo.codeValue, cardInfo.codeType, sizePx, sizePx).asImageBitmap()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (bitmap != null) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = generateCode(cardInfo.codeValue, cardInfo.codeType, widthPx, heightPx).asImageBitmap(),
|
bitmap = bitmap,
|
||||||
contentDescription = "Card code"
|
contentDescription = cardInfo.codeValue,
|
||||||
|
modifier = Modifier.size(safeSize)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
"Failed to generate code",
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user