package ru.omni_devel.cards import android.content.Intent import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalActivity import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleOut import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.interaction.MutableInteractionSource 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.RowScope 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.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonElevation import androidx.compose.material3.DrawerValue import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.QrCode import androidx.compose.material.icons.outlined.DensityMedium import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.graphics.toColorInt import kotlinx.coroutines.launch @Composable fun Page( name: String, innerPadding: PaddingValues, floatingActionButton: @Composable (() -> Unit) = {}, content: @Composable () -> Unit ) { Scaffold( floatingActionButton = floatingActionButton ) { padding -> Box( modifier = Modifier .padding(padding) .fillMaxSize() .background(MaterialTheme.colorScheme.background) .padding(horizontal = 16.dp) .padding(top = 16.dp) ) { Column( verticalArrangement = Arrangement.spacedBy(8.dp) ) { content() } } } } @Composable fun ExpandableFab( items: List ) { val activityContext = LocalActivity.current var isExpanded by remember { mutableStateOf(false) } val rotation by animateFloatAsState(if (isExpanded) 45f else 0f) Column( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(8.dp) ) { items.forEach { page -> AnimatedVisibility( visible = isExpanded, enter = fadeIn() + scaleIn(), exit = fadeOut() + scaleOut() ) { ExtendedFloatingActionButton( text = { Text(page.name) }, icon = { Icon(page.icon, contentDescription = null) }, onClick = { isExpanded = false if (page.pageClass != null) { val intent = Intent(activityContext, page.pageClass) activityContext!!.startActivity(intent) } else if (page.onClick != null) { page.onClick() } } ) } } FloatingActionButton( onClick = { isExpanded = !isExpanded } ) { Icon( modifier = Modifier.graphicsLayer { rotationZ = rotation }, imageVector = Icons.Outlined.Add, contentDescription = null ) } } } @Composable fun TopBar(text: String, innerPadding: PaddingValues, openMenuButton: (@Composable () -> Unit)? = null) { Row( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.primaryContainer) .padding(top = innerPadding.calculateTopPadding()) .padding(vertical = 12.dp, horizontal = 12.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { if (openMenuButton != null) { openMenuButton() } Text( text, modifier = Modifier, fontSize = 24.sp, color = MaterialTheme.colorScheme.onPrimaryContainer ) } } @Composable fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit) { val context = LocalContext.current var menuExpanded by remember { mutableStateOf(false) } val cardColor = if (cardInfo.color == null) MaterialTheme.colorScheme.primaryContainer else Color(cardInfo.color.toColorInt()) Box(modifier = Modifier.fillMaxWidth()) { Surface( modifier = Modifier .fillMaxWidth() .height(64.dp) .pointerInput(Unit) { detectTapGestures( onTap = { val intent = Intent(context, ShowCardActivity::class.java) intent.putExtra("cardId", cardInfo.id) context.startActivity(intent) }, onLongPress = { menuExpanded = true } ) }, shape = RoundedCornerShape(12.dp), color = cardColor.copy(0.425f), tonalElevation = 2.dp, ) { Row( modifier = Modifier .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( cardInfo.name, fontSize = 16.sp, color = MaterialTheme.colorScheme.onPrimaryContainer, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Start ) } } DropdownMenu( expanded = menuExpanded, onDismissRequest = { menuExpanded = false } ) { DropdownMenuItem( text = { Text(stringResource(R.string.do_edit_card)) }, onClick = { menuExpanded = false val intent = Intent(context, EditCardActivity::class.java) intent.putExtra("cardId", cardInfo.id) context.startActivity(intent) } ) DropdownMenuItem( text = { Text(stringResource(R.string.do_delete_card)) }, onClick = { menuExpanded = false onDeleteCard(cardInfo.id) } ) } } } @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 fun AdaptiveButton( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource? = null, content: @Composable RowScope.() -> Unit ) { Button( onClick = onClick, modifier = modifier, enabled = enabled, shape = RoundedCornerShape(12.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primaryContainer, contentColor = MaterialTheme.colorScheme.onPrimaryContainer ), elevation = elevation, border = border, contentPadding = contentPadding, interactionSource = interactionSource, content = content ) }