363 lines
12 KiB
Kotlin
363 lines
12 KiB
Kotlin
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.Spacer
|
|
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.layout.width
|
|
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.material.icons.outlined.DragHandle
|
|
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.draw.clip
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.ImageBitmap
|
|
import androidx.compose.ui.graphics.graphicsLayer
|
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
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,
|
|
topBarAdditionalElements: @Composable () -> Unit = {},
|
|
floatingActionButton: @Composable (() -> Unit) = {},
|
|
content: @Composable () -> Unit
|
|
) {
|
|
Scaffold(
|
|
topBar = {
|
|
TopBar(name, innerPadding, topBarAdditionalElements)
|
|
},
|
|
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<NavigationPageData>
|
|
) {
|
|
val activityContext = LocalActivity.current
|
|
var isExpanded by remember { mutableStateOf(false) }
|
|
|
|
val rotation by animateFloatAsState(if (isExpanded) 45f else 0f)
|
|
|
|
BackHandler(
|
|
enabled = isExpanded
|
|
) {
|
|
isExpanded = false
|
|
}
|
|
|
|
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,
|
|
topBarAdditionalElements: @Composable () -> Unit = {}
|
|
) {
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.background(MaterialTheme.colorScheme.primaryContainer)
|
|
.padding(top = innerPadding.calculateTopPadding())
|
|
.padding(vertical = 12.dp, horizontal = 12.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.SpaceBetween
|
|
) {
|
|
Text(
|
|
text,
|
|
modifier = Modifier,
|
|
fontSize = 24.sp,
|
|
color = MaterialTheme.colorScheme.onPrimaryContainer
|
|
)
|
|
|
|
topBarAdditionalElements()
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit, reorderIcon: (@Composable () -> Unit)? = null) {
|
|
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 = MaterialTheme.colorScheme.primaryContainer.copy(0.625f),
|
|
tonalElevation = 2.dp,
|
|
) {
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.background(Color.Transparent)
|
|
.padding(horizontal = 8.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.SpaceBetween
|
|
) {
|
|
Row(
|
|
modifier = Modifier.weight(1f),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
) {
|
|
CardIcon(
|
|
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
|
|
)
|
|
}
|
|
|
|
if (reorderIcon != null) {
|
|
reorderIcon()
|
|
}
|
|
}
|
|
}
|
|
|
|
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),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Center
|
|
) {
|
|
if (isLoading) {
|
|
CircularProgressIndicator(
|
|
modifier = Modifier.size(32.dp),
|
|
color = MaterialTheme.colorScheme.primary
|
|
)
|
|
} else if (icon == null) {
|
|
Icon(
|
|
modifier = Modifier.size(32.dp),
|
|
imageVector = Icons.Outlined.QrCode,
|
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
|
contentDescription = null
|
|
)
|
|
} else {
|
|
Image(
|
|
bitmap = icon,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(36.dp).clip(RoundedCornerShape(12.dp))
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun AdaptiveButton(
|
|
onClick: () -> Unit,
|
|
modifier: Modifier = Modifier,
|
|
enabled: Boolean = true,
|
|
icon: ImageVector? = null,
|
|
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
|
|
border: BorderStroke? = null,
|
|
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
|
interactionSource: MutableInteractionSource? = null,
|
|
isSecondary: Boolean = false,
|
|
content: @Composable RowScope.() -> Unit
|
|
) {
|
|
val colors = if (isSecondary)
|
|
ButtonDefaults.buttonColors(
|
|
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
|
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
|
)
|
|
else
|
|
ButtonDefaults.buttonColors(
|
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
|
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
|
)
|
|
|
|
Button(
|
|
onClick = onClick,
|
|
modifier = modifier,
|
|
enabled = enabled,
|
|
shape = RoundedCornerShape(12.dp),
|
|
colors = colors,
|
|
elevation = elevation,
|
|
border = border,
|
|
contentPadding = contentPadding,
|
|
interactionSource = interactionSource,
|
|
content = {
|
|
if (icon != null) {
|
|
Icon(
|
|
imageVector = icon,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(18.dp)
|
|
)
|
|
Spacer(modifier = Modifier.width(8.dp))
|
|
}
|
|
|
|
content()
|
|
}
|
|
)
|
|
}
|