forked from omni/OmniCards
feat: added icons for cards
This commit is contained in:
@@ -77,7 +77,7 @@ dependencies {
|
|||||||
implementation("com.google.android.gms:play-services-wearable:18.1.0")
|
implementation("com.google.android.gms:play-services-wearable:18.1.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
|
||||||
implementation(libs.compose.colorpicker)
|
implementation(libs.compose.colorpicker)
|
||||||
implementation("org.burnoutcrew.composereorderable:reorderable:0.9.6")
|
|
||||||
implementation("io.ktor:ktor-client-core:3.5.0")
|
implementation("io.ktor:ktor-client-core:3.5.0")
|
||||||
implementation("io.ktor:ktor-client-cio:3.5.0")
|
implementation("io.ktor:ktor-client-cio:3.5.0")
|
||||||
|
implementation("androidx.compose.material:material-icons-extended")
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,11 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.OmniCards">
|
android:theme="@style/Theme.OmniCards">
|
||||||
|
<activity
|
||||||
|
android:name=".SelectIconActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/title_activity_select_icon"
|
||||||
|
android:theme="@style/Theme.OmniCards" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".FillTemplateActivity"
|
android:name=".FillTemplateActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.activity.enableEdgeToEdge
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package ru.omni_devel.cards
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.json.JSONArray
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class CardIcon(
|
||||||
|
val names: LinkedHashMap<String, String>,
|
||||||
|
val iconId: String? = null
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun parseIconsDataFromJson(json: String): List<CardIcon> {
|
||||||
|
val array = JSONArray(json)
|
||||||
|
val icons = mutableListOf<CardIcon>()
|
||||||
|
|
||||||
|
for (i in 0 until array.length()) {
|
||||||
|
val obj = array.getJSONObject(i)
|
||||||
|
|
||||||
|
icons.add(CardIcon(
|
||||||
|
names = jsonObjectToMap(obj.getJSONObject("names")),
|
||||||
|
iconId = if (obj.has("iconId")) obj.getString("iconId") else null
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return icons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,5 +14,6 @@ class CardInfo (
|
|||||||
val name: String,
|
val name: String,
|
||||||
val codeType: BarcodeFormat,
|
val codeType: BarcodeFormat,
|
||||||
val codeValue: String,
|
val codeValue: String,
|
||||||
val color: String?
|
val color: String?,
|
||||||
|
val icon: String?
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
package ru.omni_devel.cards
|
package ru.omni_devel.cards
|
||||||
|
|
||||||
const val TEMPLATES_URL = "https://gitea.omni-devel.ru/omni/OmniCardsRepo/raw/branch/main/templates.json"
|
const val REPO_BASE_URL = "https://gitea.omni-devel.ru/omni/OmniCardsRepo/raw/branch/main"
|
||||||
|
|
||||||
val SPACE_SYMBOLS = listOf(" ", "-", "_")
|
const val TEMPLATES_REPO_FILE = "templates.json"
|
||||||
|
|
||||||
|
const val ICONS_REPO_FILE = "icons.json"
|
||||||
|
const val ICONS_REPO_FOLDER = "icons"
|
||||||
|
|
||||||
|
val SEARCH_REPLACE_SYMBOLS = mapOf(
|
||||||
|
" " to "",
|
||||||
|
"-" to "",
|
||||||
|
"_" to "",
|
||||||
|
"ё" to "е"
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ import android.content.ContentValues
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import androidx.core.database.getStringOrNull
|
||||||
import com.google.zxing.BarcodeFormat
|
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?) {
|
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)")
|
db!!.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS cards (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, codeType TEXT, codeValue TEXT, color TEXT, icon TEXT)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpgrade(
|
override fun onUpgrade(
|
||||||
@@ -35,9 +38,13 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
|||||||
db.execSQL("ALTER TABLE cards DROP COLUMN monochrome")
|
db.execSQL("ALTER TABLE cards DROP COLUMN monochrome")
|
||||||
db.execSQL("ALTER TABLE cards DROP COLUMN position")
|
db.execSQL("ALTER TABLE cards DROP COLUMN position")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 6) {
|
||||||
|
db!!.execSQL("ALTER TABLE cards ADD COLUMN icon TEXT")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCard(name: String, codeValue: String, codeType: BarcodeFormat, color: String?) {
|
fun addCard(name: String, codeValue: String, codeType: BarcodeFormat, color: String?, icon: String?) {
|
||||||
val db = this.writableDatabase
|
val db = this.writableDatabase
|
||||||
|
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
@@ -46,19 +53,21 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
|||||||
values.put("codeValue", codeValue)
|
values.put("codeValue", codeValue)
|
||||||
values.put("codeType", codeType.name)
|
values.put("codeType", codeType.name)
|
||||||
values.put("color", color)
|
values.put("color", color)
|
||||||
|
values.put("icon", icon)
|
||||||
|
|
||||||
db.insert("cards", null, values)
|
db.insert("cards", null, values)
|
||||||
|
|
||||||
db.close()
|
db.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun editCard(id: Int, name: String, codeValue: String, codeType: BarcodeFormat, color: String?) {
|
fun editCard(id: Int, name: String, codeValue: String, codeType: BarcodeFormat, color: String?, icon: String?) {
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
|
|
||||||
values.put("name", name)
|
values.put("name", name)
|
||||||
values.put("codeValue", codeValue)
|
values.put("codeValue", codeValue)
|
||||||
values.put("codeType", codeType.name)
|
values.put("codeType", codeType.name)
|
||||||
values.put("color", color)
|
values.put("color", color)
|
||||||
|
values.put("icon", icon)
|
||||||
|
|
||||||
val db = this.writableDatabase
|
val db = this.writableDatabase
|
||||||
|
|
||||||
@@ -79,7 +88,8 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
|||||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color"))
|
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||||
|
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +116,8 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
|||||||
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
name = cursor.getString(cursor.getColumnIndexOrThrow("name")),
|
||||||
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
codeType = BarcodeFormat.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("codeType"))),
|
||||||
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
codeValue = cursor.getString(cursor.getColumnIndexOrThrow("codeValue")),
|
||||||
color = cursor.getString(cursor.getColumnIndexOrThrow("color"))
|
color = cursor.getString(cursor.getColumnIndexOrThrow("color")),
|
||||||
|
icon = cursor.getStringOrNull(cursor.getColumnIndexOrThrow("icon"))
|
||||||
)
|
)
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
@@ -140,10 +151,11 @@ class DbHelper(val context: Context, val factory: SQLiteDatabase.CursorFactory?)
|
|||||||
values.put("codeValue", card.codeValue)
|
values.put("codeValue", card.codeValue)
|
||||||
values.put("codeType", card.codeType.name)
|
values.put("codeType", card.codeType.name)
|
||||||
values.put("color", card.color)
|
values.put("color", card.color)
|
||||||
|
values.put("icon", card.icon)
|
||||||
|
|
||||||
db.insert("cards", null, values)
|
db.insert("cards", null, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.close()
|
db.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -18,10 +19,13 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
@@ -31,7 +35,9 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -73,13 +79,16 @@ fun CardEditable(
|
|||||||
codeType: BarcodeFormat,
|
codeType: BarcodeFormat,
|
||||||
onCodeTypeChange: (BarcodeFormat) -> Unit,
|
onCodeTypeChange: (BarcodeFormat) -> Unit,
|
||||||
color: String?,
|
color: String?,
|
||||||
onColorChange: (String?) -> Unit
|
onColorChange: (String?) -> Unit,
|
||||||
|
icon: String?,
|
||||||
|
onIconChange: (String?) -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
var isCodeTypeDropdownExpanded by rememberSaveable { mutableStateOf(false) }
|
var isCodeTypeDropdownExpanded by rememberSaveable { mutableStateOf(false) }
|
||||||
var isColorEnabled by rememberSaveable { mutableStateOf(color != null) }
|
var isColorEnabled by rememberSaveable { mutableStateOf(color != null) }
|
||||||
var currentColor by rememberSaveable { mutableStateOf(color) }
|
var currentColor by rememberSaveable { mutableStateOf(color) }
|
||||||
|
var currentIcon by rememberSaveable { mutableStateOf(icon) }
|
||||||
|
|
||||||
val fieldModifier = Modifier.fillMaxWidth()
|
val fieldModifier = Modifier.fillMaxWidth()
|
||||||
|
|
||||||
@@ -118,6 +127,16 @@ fun CardEditable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val selectIconLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
val icon = result.data?.getStringExtra("icon")
|
||||||
|
|
||||||
|
onIconChange(icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = cardName,
|
value = cardName,
|
||||||
onValueChange = onCardNameChange,
|
onValueChange = onCardNameChange,
|
||||||
@@ -221,6 +240,29 @@ fun CardEditable(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdaptiveButton(
|
||||||
|
modifier = Modifier.fillMaxWidth().height(64.dp),
|
||||||
|
contentPadding = PaddingValues(horizontal = 0.dp),
|
||||||
|
onClick = {
|
||||||
|
val intent = Intent(context, SelectIconActivity::class.java)
|
||||||
|
|
||||||
|
selectIconLauncher.launch(intent)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Transparent)
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
CardIcon(if (icon == null) null else base64ToImageBitmap(icon), MaterialTheme.colorScheme.primary)
|
||||||
|
|
||||||
|
Text(stringResource(R.string.do_select_icon))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -229,6 +271,7 @@ fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
|||||||
var codeValue by rememberSaveable { mutableStateOf("") }
|
var codeValue by rememberSaveable { mutableStateOf("") }
|
||||||
var codeType by rememberSaveable { mutableStateOf(BarcodeFormat.QR_CODE) }
|
var codeType by rememberSaveable { mutableStateOf(BarcodeFormat.QR_CODE) }
|
||||||
var color by rememberSaveable { mutableStateOf<String?>(null) }
|
var color by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
|
var icon by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
val context = LocalActivity.current
|
val context = LocalActivity.current
|
||||||
|
|
||||||
@@ -248,7 +291,9 @@ fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
|||||||
codeType = codeType,
|
codeType = codeType,
|
||||||
onCodeTypeChange = { codeType = it },
|
onCodeTypeChange = { codeType = it },
|
||||||
color = color,
|
color = color,
|
||||||
onColorChange = { color = it }
|
onColorChange = { color = it },
|
||||||
|
icon = icon,
|
||||||
|
onIconChange = { icon = it }
|
||||||
)
|
)
|
||||||
|
|
||||||
AdaptiveButton(
|
AdaptiveButton(
|
||||||
@@ -259,7 +304,7 @@ fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) {
|
|||||||
val err = checkCardData(cardName, codeValue)
|
val err = checkCardData(cardName, codeValue)
|
||||||
|
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
getDbFun().addCard(cardName, codeValue, codeType, color)
|
getDbFun().addCard(cardName, codeValue, codeType, color, icon)
|
||||||
context!!.finish()
|
context!!.finish()
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show()
|
||||||
@@ -278,6 +323,7 @@ fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> Db
|
|||||||
var codeValue by rememberSaveable { mutableStateOf(cardInfo.codeValue) }
|
var codeValue by rememberSaveable { mutableStateOf(cardInfo.codeValue) }
|
||||||
var codeType by rememberSaveable { mutableStateOf(cardInfo.codeType) }
|
var codeType by rememberSaveable { mutableStateOf(cardInfo.codeType) }
|
||||||
var color by rememberSaveable { mutableStateOf(cardInfo.color) }
|
var color by rememberSaveable { mutableStateOf(cardInfo.color) }
|
||||||
|
var icon by rememberSaveable { mutableStateOf(cardInfo.icon) }
|
||||||
|
|
||||||
val context = LocalActivity.current
|
val context = LocalActivity.current
|
||||||
|
|
||||||
@@ -297,7 +343,9 @@ fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> Db
|
|||||||
codeType = codeType,
|
codeType = codeType,
|
||||||
onCodeTypeChange = { codeType = it },
|
onCodeTypeChange = { codeType = it },
|
||||||
color = color,
|
color = color,
|
||||||
onColorChange = { color = it }
|
onColorChange = { color = it },
|
||||||
|
icon = icon,
|
||||||
|
onIconChange = { icon = it }
|
||||||
)
|
)
|
||||||
|
|
||||||
AdaptiveButton(
|
AdaptiveButton(
|
||||||
@@ -308,7 +356,7 @@ fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> Db
|
|||||||
val err = checkCardData(cardName, codeValue)
|
val err = checkCardData(cardName, codeValue)
|
||||||
|
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
getDbFun().editCard(cardInfo.id, cardName, codeValue, codeType, color)
|
getDbFun().editCard(cardInfo.id, cardName, codeValue, codeType, color, icon)
|
||||||
context!!.finish()
|
context!!.finish()
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, context!!.getString(err), Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.Intent
|
|||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
@@ -11,24 +12,26 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonColors
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.ButtonElevation
|
import androidx.compose.material3.ButtonElevation
|
||||||
import androidx.compose.material3.DrawerValue
|
import androidx.compose.material3.DrawerValue
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material.icons.outlined.QrCode
|
||||||
|
import androidx.compose.material.icons.outlined.DensityMedium
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalDrawerSheet
|
import androidx.compose.material3.ModalDrawerSheet
|
||||||
import androidx.compose.material3.ModalNavigationDrawer
|
import androidx.compose.material3.ModalNavigationDrawer
|
||||||
@@ -45,11 +48,10 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier.Companion
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -105,15 +107,15 @@ fun Page(name: String, innerPadding: PaddingValues, pages: List<NavigationPageDa
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopBar(name, innerPadding, if (pages != null) {
|
TopBar(name, innerPadding, if (pages != null) {
|
||||||
{
|
{
|
||||||
Text(
|
Icon(
|
||||||
"☰",
|
|
||||||
fontSize = 24.sp,
|
|
||||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
|
||||||
modifier = Modifier.clickable() {
|
modifier = Modifier.clickable() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
drawerState.open()
|
drawerState.open()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
imageVector = Icons.Outlined.DensityMedium,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else null)
|
} else null)
|
||||||
@@ -167,6 +169,11 @@ fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
|||||||
|
|
||||||
var menuExpanded by remember { mutableStateOf(false) }
|
var menuExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val cardColor = if (cardInfo.color == null)
|
||||||
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
|
else
|
||||||
|
Color(cardInfo.color.toColorInt())
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxWidth()) {
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -185,20 +192,27 @@ fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
color = (if (cardInfo.color == null) MaterialTheme.colorScheme.primaryContainer else Color(cardInfo.color.toColorInt())).copy(0.425f),
|
color = cardColor.copy(0.425f),
|
||||||
tonalElevation = 2.dp,
|
tonalElevation = 2.dp,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
verticalAlignment = Alignment.CenterVertically
|
.fillMaxSize()
|
||||||
|
.background(Color.Transparent)
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
|
CardIcon(
|
||||||
|
if (cardInfo.icon == null) null else base64ToImageBitmap(cardInfo.icon),
|
||||||
|
cardColor = cardColor
|
||||||
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
cardInfo.name,
|
cardInfo.name,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.padding(16.dp)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
textAlign = TextAlign.Start
|
textAlign = TextAlign.Start
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -232,12 +246,46 @@ fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardIcon(icon: ImageBitmap?, cardColor: Color, isLoading: Boolean = false) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
cardColor.copy(0.525f),
|
||||||
|
RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(32.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (isLoading) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
} else if (icon == null) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
imageVector = Icons.Outlined.QrCode,
|
||||||
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
bitmap = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AdaptiveButton(
|
fun AdaptiveButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
shape: Shape = ButtonDefaults.shape,
|
|
||||||
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
|
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
|
||||||
border: BorderStroke? = null,
|
border: BorderStroke? = null,
|
||||||
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
||||||
@@ -248,7 +296,7 @@ fun AdaptiveButton(
|
|||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
shape = shape,
|
shape = RoundedCornerShape(12.dp),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
|||||||
@@ -6,44 +6,22 @@ import android.os.Bundle
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
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.verticalScroll
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.engine.cio.CIO
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.statement.HttpResponse
|
|
||||||
import io.ktor.client.statement.bodyAsText
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||||
|
|
||||||
class FillTemplateActivity : ComponentActivity() {
|
class FillTemplateActivity : ComponentActivity() {
|
||||||
@@ -76,7 +54,7 @@ fun FillTemplatePage(template: Template, innerPadding: PaddingValues) {
|
|||||||
}) }
|
}) }
|
||||||
|
|
||||||
Page(
|
Page(
|
||||||
selectTemplateNameViaLanguage(template.names, language),
|
selectItemNameViaLanguage(template.names, language),
|
||||||
innerPadding
|
innerPadding
|
||||||
) {
|
) {
|
||||||
for (field in template.fields) {
|
for (field in template.fields) {
|
||||||
@@ -88,7 +66,7 @@ fun FillTemplatePage(template: Template, innerPadding: PaddingValues) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
label = { Text((if (field.maybeEmpty) "" else "*") + selectTemplateNameViaLanguage(field.names, language)) }
|
label = { Text((if (field.maybeEmpty) "" else "*") + selectItemNameViaLanguage(field.names, language)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package ru.omni_devel.cards
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.LocalActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.QrCode
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
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
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.engine.cio.CIO
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import ru.omni_devel.cards.ui.theme.OmniCardsTheme
|
||||||
|
|
||||||
|
class SelectIconActivity : ComponentActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
setContent {
|
||||||
|
OmniCardsTheme {
|
||||||
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
|
SelectIconPage(innerPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SelectIconPage(innerPadding: PaddingValues) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val activityContext = LocalActivity.current
|
||||||
|
|
||||||
|
val language = getLanguage(context)
|
||||||
|
|
||||||
|
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||||
|
|
||||||
|
var iconsData by remember { mutableStateOf<List<CardIcon>?>(null) }
|
||||||
|
val loadedIcons = remember { mutableStateMapOf<String, ImageBitmap?>() }
|
||||||
|
var loadingError by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
try {
|
||||||
|
val client = HttpClient(CIO)
|
||||||
|
|
||||||
|
val response: HttpResponse = client.get("$REPO_BASE_URL/$ICONS_REPO_FILE")
|
||||||
|
|
||||||
|
val loadedIconsData = CardIcon.parseIconsDataFromJson(response.bodyAsText())
|
||||||
|
iconsData = loadedIconsData
|
||||||
|
|
||||||
|
for (iconData in loadedIconsData) {
|
||||||
|
if (iconData.iconId != null) {
|
||||||
|
val response = client.get("$REPO_BASE_URL/icons/${iconData.iconId}.png")
|
||||||
|
val bytes: ByteArray = response.body()
|
||||||
|
|
||||||
|
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)?.let { bitmap ->
|
||||||
|
loadedIcons[iconData.iconId] = bitmap.asImageBitmap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
loadingError = e.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Page(context.getString(R.string.choosing_icon), innerPadding) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = searchQuery,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
searchQuery = newValue
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
label = { Text(stringResource(R.string.search)) }
|
||||||
|
)
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
if (loadingError != null) {
|
||||||
|
item {
|
||||||
|
Text(loadingError!!)
|
||||||
|
}
|
||||||
|
} else if (iconsData == null) {
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(48.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val searchResults = iconsData!!.filter { iconData ->
|
||||||
|
var queryCleaned = searchQuery.lowercase()
|
||||||
|
|
||||||
|
if (queryCleaned.isEmpty()) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name in iconData.names.values) {
|
||||||
|
var nameCleaned = name.lowercase()
|
||||||
|
|
||||||
|
for ((from, to) in SEARCH_REPLACE_SYMBOLS) {
|
||||||
|
nameCleaned = nameCleaned.replace(from, to)
|
||||||
|
queryCleaned = queryCleaned.replace(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameCleaned.contains(queryCleaned)) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@filter false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchResults.isEmpty()) {
|
||||||
|
item {
|
||||||
|
Text(stringResource(R.string.icons_not_found))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (iconData in searchResults) {
|
||||||
|
item {
|
||||||
|
val loadedIcon = loadedIcons[iconData.iconId]
|
||||||
|
|
||||||
|
AdaptiveButton(
|
||||||
|
modifier = Modifier.fillMaxWidth().height(64.dp),
|
||||||
|
contentPadding = PaddingValues(0.dp),
|
||||||
|
onClick = {
|
||||||
|
if (loadedIcon == null && iconData.iconId != null) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.wait_for_icon_load), Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
val intent = Intent()
|
||||||
|
|
||||||
|
if (loadedIcon != null) {
|
||||||
|
intent.putExtra("icon", imageBitmapToBase64(loadedIcon))
|
||||||
|
}
|
||||||
|
|
||||||
|
activityContext!!.setResult(Activity.RESULT_OK, intent)
|
||||||
|
activityContext.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Transparent)
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
CardIcon(loadedIcon, MaterialTheme.colorScheme.primary, loadedIcon == null && iconData.iconId != null)
|
||||||
|
|
||||||
|
Text(selectItemNameViaLanguage(iconData.names, language))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ fun SelectTemplatePage(innerPadding: PaddingValues) {
|
|||||||
try {
|
try {
|
||||||
val client = HttpClient(CIO)
|
val client = HttpClient(CIO)
|
||||||
|
|
||||||
val response: HttpResponse = client.get(TEMPLATES_URL)
|
val response: HttpResponse = client.get("$REPO_BASE_URL/$TEMPLATES_REPO_FILE")
|
||||||
|
|
||||||
templates = Template.parseTemplatesFromJson(response.bodyAsText())
|
templates = Template.parseTemplatesFromJson(response.bodyAsText())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -104,7 +104,7 @@ fun SelectTemplatePage(innerPadding: PaddingValues) {
|
|||||||
searchQuery = newValue
|
searchQuery = newValue
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
label = { Text(stringResource(R.string.search_templates)) }
|
label = { Text(stringResource(R.string.search)) }
|
||||||
)
|
)
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -139,9 +139,9 @@ fun SelectTemplatePage(innerPadding: PaddingValues) {
|
|||||||
for (name in template.names.values) {
|
for (name in template.names.values) {
|
||||||
var nameCleaned = name.lowercase()
|
var nameCleaned = name.lowercase()
|
||||||
|
|
||||||
for (spaceSymbol in SPACE_SYMBOLS) {
|
for ((from, to) in SEARCH_REPLACE_SYMBOLS) {
|
||||||
nameCleaned = nameCleaned.replace(spaceSymbol, "")
|
nameCleaned = nameCleaned.replace(from, to)
|
||||||
queryCleaned = queryCleaned.replace(spaceSymbol, "")
|
queryCleaned = queryCleaned.replace(from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameCleaned.contains(queryCleaned)) {
|
if (nameCleaned.contains(queryCleaned)) {
|
||||||
@@ -169,7 +169,7 @@ fun SelectTemplatePage(innerPadding: PaddingValues) {
|
|||||||
fillTemplateLauncher.launch(intent)
|
fillTemplateLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(selectTemplateNameViaLanguage(template.names, language))
|
Text(selectItemNameViaLanguage(template.names, language))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,6 @@ import kotlinx.serialization.Serializable
|
|||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
private fun jsonObjectToMap(obj: JSONObject): LinkedHashMap<String, String> {
|
|
||||||
val map = LinkedHashMap<String, String>()
|
|
||||||
val keys = obj.keys()
|
|
||||||
|
|
||||||
while (keys.hasNext()) {
|
|
||||||
val key = keys.next()
|
|
||||||
map[key] = obj.getString(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Field(
|
class Field(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
|||||||
@@ -2,12 +2,19 @@ package ru.omni_devel.cards
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.LocaleList
|
import android.os.LocaleList
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asAndroidBitmap
|
||||||
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.EncodeHintType
|
import com.google.zxing.EncodeHintType
|
||||||
import com.google.zxing.MultiFormatWriter
|
import com.google.zxing.MultiFormatWriter
|
||||||
import com.journeyapps.barcodescanner.BarcodeEncoder
|
import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import android.util.Base64
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
|
||||||
fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
fun generateCode(value: String, format: BarcodeFormat): Bitmap {
|
||||||
val writer = MultiFormatWriter()
|
val writer = MultiFormatWriter()
|
||||||
@@ -81,6 +88,36 @@ fun getLanguage(context: Context): String {
|
|||||||
return languageCode
|
return languageCode
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectTemplateNameViaLanguage(names: LinkedHashMap<String, String>, language: String): String {
|
fun selectItemNameViaLanguage(names: LinkedHashMap<String, String>, language: String): String {
|
||||||
return names[language] ?: names.values.firstOrNull() ?: "???"
|
return names[language] ?: names.values.firstOrNull() ?: "???"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun jsonObjectToMap(obj: JSONObject): LinkedHashMap<String, String> {
|
||||||
|
val map = LinkedHashMap<String, String>()
|
||||||
|
val keys = obj.keys()
|
||||||
|
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
val key = keys.next()
|
||||||
|
map[key] = obj.getString(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
fun imageBitmapToBase64(imageBitmap: ImageBitmap): String {
|
||||||
|
val bitmap = imageBitmap.asAndroidBitmap()
|
||||||
|
val outputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
||||||
|
|
||||||
|
val byteArray = outputStream.toByteArray()
|
||||||
|
|
||||||
|
return Base64.encodeToString(byteArray, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun base64ToImageBitmap(base64String: String): ImageBitmap {
|
||||||
|
val decodedBytes = Base64.decode(base64String, Base64.DEFAULT)
|
||||||
|
val bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
|
||||||
|
|
||||||
|
return bitmap.asImageBitmap()
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,7 +39,11 @@
|
|||||||
<string name="choosing_template">Выбор шаблона</string>
|
<string name="choosing_template">Выбор шаблона</string>
|
||||||
<string name="error_in_data">Ошибка в данных. Пожалуйста, заполните все необходимые (*) поля</string>
|
<string name="error_in_data">Ошибка в данных. Пожалуйста, заполните все необходимые (*) поля</string>
|
||||||
<string name="templates_not_found">Шаблоны не найдены</string>
|
<string name="templates_not_found">Шаблоны не найдены</string>
|
||||||
<string name="search_templates">Поиск шаблонов</string>
|
<string name="search">Поиск</string>
|
||||||
|
<string name="choosing_icon">Выбор иконки</string>
|
||||||
|
<string name="icons_not_found">Иконки не найдены</string>
|
||||||
|
<string name="do_select_icon">Выбрать иконку</string>
|
||||||
|
<string name="wait_for_icon_load">Дождитесь загрузки иконки</string>
|
||||||
<string name="card_name_should_not_be_empty">Имя карты не должно быть пустым</string>
|
<string name="card_name_should_not_be_empty">Имя карты не должно быть пустым</string>
|
||||||
<string name="card_value_should_not_be_empty">Значение карты не должно быть пустым</string>
|
<string name="card_value_should_not_be_empty">Значение карты не должно быть пустым</string>
|
||||||
<string name="name_must_be_shorter_than_32_symbols">Имя карты должно быть короче 32 символов</string>
|
<string name="name_must_be_shorter_than_32_symbols">Имя карты должно быть короче 32 символов</string>
|
||||||
|
|||||||
@@ -44,10 +44,15 @@
|
|||||||
<string name="choosing_template">Choosing template</string>
|
<string name="choosing_template">Choosing template</string>
|
||||||
<string name="error_in_data">Error in data. Please, fill all required (*) fields</string>
|
<string name="error_in_data">Error in data. Please, fill all required (*) fields</string>
|
||||||
<string name="templates_not_found">Templates not found</string>
|
<string name="templates_not_found">Templates not found</string>
|
||||||
<string name="search_templates">Search templates</string>
|
<string name="search">Search</string>
|
||||||
|
<string name="choosing_icon">Choosing icon</string>
|
||||||
|
<string name="icons_not_found">Icons not found</string>
|
||||||
|
<string name="do_select_icon">Select icon</string>
|
||||||
|
<string name="wait_for_icon_load">Wait for icon load</string>
|
||||||
<string name="card_name_should_not_be_empty">Card name must not be empty</string>
|
<string name="card_name_should_not_be_empty">Card name must not be empty</string>
|
||||||
<string name="card_value_should_not_be_empty">Card value must not be empty</string>
|
<string name="card_value_should_not_be_empty">Card value must not be empty</string>
|
||||||
<string name="name_must_be_shorter_than_32_symbols">Card name must be shorter than 32 characters</string>
|
<string name="name_must_be_shorter_than_32_symbols">Card name must be shorter than 32 characters</string>
|
||||||
|
|
||||||
<string name="failed_to_generate_card_code">Failed to generate card code. Check the card data</string>
|
<string name="failed_to_generate_card_code">Failed to generate card code. Check the card data</string>
|
||||||
|
<string name="title_activity_select_icon">SelectIconActivity</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user