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 kotlinx.coroutines.launch 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?>(null) } val loadedIcons = remember { mutableStateMapOf() } var loadingError by remember { mutableStateOf(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) { launch { 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() } } var queryCleaned = searchQuery.lowercase() for ((from, to) in SEARCH_REPLACE_SYMBOLS) { queryCleaned = queryCleaned.replace(from, to) } val searchResults = iconsData?.filter { iconData -> 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) } if (nameCleaned.contains(queryCleaned)) { return@filter true } } return@filter false } ?: emptyList() 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 { if (searchResults.isEmpty()) { item { Text(stringResource(R.string.icons_not_found)) } } else { for (iconData in searchResults) { val loadedIcon = loadedIcons[iconData.iconId] item { 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 ) ) } } } } } } } } }