package com.shire.mobile import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween 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.FlowRow import androidx.compose.foundation.layout.Row 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.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.scale import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.darkColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp private val ShireBackground = Color(0xFF090B10) private val ShireSurface = Color(0xFF11151D) private val ShireRaised = Color(0xFF181D28) private val ShirePurple = Color(0xFFA987FF) private val ShireBlue = Color(0xFF73D7FF) private val ShireGreen = Color(0xFF75E6A4) private val ShireAmber = Color(0xFFFFC766) private val ShireRed = Color(0xFFFF7D86) private val ShireText = Color(0xFFF2F3F7) private val ShireMuted = Color(0xFF9EA6B5) private enum class ShireExpression { Normal, Happy, Thinking, Sentinel, Concerned } private data class ApprovalItem( val id: Int, val title: String, val stage: String, val purpose: String, val affects: String, val risk: String, val estimate: String, val reversible: String, val status: String = "Pending" ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme( colorScheme = darkColorScheme( primary = ShirePurple, secondary = ShireBlue, background = ShireBackground, surface = ShireSurface, onPrimary = Color.Black, onBackground = ShireText, onSurface = ShireText ) ) { ShireMobileApp() } } } } @Composable private fun ShireMobileApp() { var showLearnPrint by remember { mutableStateOf(false) } var showApprovals by remember { mutableStateOf(false) } var expression by remember { mutableStateOf(ShireExpression.Normal) } var message by remember { mutableStateOf("Systems ready, Chief. What are we building today?") } var approvals by remember { mutableStateOf( listOf( ApprovalItem( id = 1, title = "Gothic controller holder", stage = "Approve Learning", purpose = "Learn the geometry and support requirements needed to create a printable controller holder.", affects = "Academy planning only. No Forge generation and no printer activity.", risk = "Low. Licensing and originality checks still required.", estimate = "45–90 minutes learning time. No material cost.", reversible = "Can be cancelled before or during uncounted practice." ), ApprovalItem( id = 2, title = "Controller holder print preparation", stage = "Approve Slicing", purpose = "Prepare an approved model for Bambu P1S #1 using black PLA.", affects = "Creates a slicer profile and print preview only.", risk = "Medium. Final dimensions and supports must be checked.", estimate = "Approximately 180 g PLA and 6–8 hours.", reversible = "Slicer output can be discarded. Printing remains separately locked." ) ) ) } val pendingCount = approvals.count { it.status == "Pending" } Scaffold(containerColor = ShireBackground) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues) .verticalScroll(rememberScrollState()) .padding(horizontal = 18.dp, vertical = 14.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "SHiRE", fontSize = 29.sp, fontWeight = FontWeight.Black, letterSpacing = 5.sp ) Text( text = "MOBILE COMMAND", fontSize = 11.sp, color = ShireMuted, letterSpacing = 2.sp ) Spacer(modifier = Modifier.height(12.dp)) RealShireFace(expression = expression) Spacer(modifier = Modifier.height(12.dp)) Text( text = message, modifier = Modifier.widthIn(max = 700.dp), textAlign = TextAlign.Center, fontSize = 17.sp, lineHeight = 23.sp ) Spacer(modifier = Modifier.height(15.dp)) StatusPanel(pendingCount) Spacer(modifier = Modifier.height(14.dp)) MainActions( pendingCount = pendingCount, onLearnPrint = { expression = ShireExpression.Thinking showLearnPrint = true }, onApprovals = { expression = ShireExpression.Thinking showApprovals = true }, onShire = { expression = ShireExpression.Normal message = "Local SHiRE mode selected. Private connection comes next." }, onChatGpt = { expression = ShireExpression.Thinking message = "ChatGPT mode selected. Cloud routing is not enabled yet." }, onBoth = { expression = ShireExpression.Happy message = "Dual-agent mode selected. SHiRE and ChatGPT will collaborate here." } ) Spacer(modifier = Modifier.height(15.dp)) RecommendedLearningCard( onPlan = { approvals = approvals + ApprovalItem( id = (approvals.maxOfOrNull { it.id } ?: 0) + 1, title = "Parametric phone support geometry", stage = "Approve Learning", purpose = "Build reusable phone-fit and support geometry skills for DockForge and Forge.", affects = "Adds a proposed Academy learning plan only.", risk = "Low. No certification or Forge permission changes.", estimate = "45–90 minutes.", reversible = "Can be rejected, deferred or cancelled before learning." ) expression = ShireExpression.Happy message = "Recommendation added to the Approval Centre. Nothing has started." }, onDefer = { expression = ShireExpression.Normal message = "Recommendation deferred. I will not keep repeating it." } ) Spacer(modifier = Modifier.height(22.dp)) ExpressionTester( onNormal = { expression = ShireExpression.Normal }, onHappy = { expression = ShireExpression.Happy }, onThinking = { expression = ShireExpression.Thinking }, onSentinel = { expression = ShireExpression.Sentinel } ) Spacer(modifier = Modifier.height(24.dp)) Text( text = "v0.2.0 • APPROVAL-GATED • PRIVATE BY DESIGN", fontSize = 10.sp, color = ShireMuted, letterSpacing = 1.sp ) } } if (showLearnPrint) { LearnAndPrintDialog( onDismiss = { showLearnPrint = false expression = ShireExpression.Normal }, onCreatePlan = { request, mode -> approvals = approvals + ApprovalItem( id = (approvals.maxOfOrNull { it.id } ?: 0) + 1, title = request.take(55).ifBlank { "New Learn & Print mission" }, stage = "Approve Learning", purpose = request, affects = "Creates a Safe Plan only. No learning, modelling, slicing or printing begins.", risk = "Pending SHiRE safety, licensing and feasibility analysis.", estimate = mode, reversible = "Fully cancellable before explicit staged approval." ) showLearnPrint = false expression = ShireExpression.Happy message = "Safe Plan added to Approval Centre. Nothing has started." } ) } if (showApprovals) { ApprovalCentreDialog( approvals = approvals, onDismiss = { showApprovals = false expression = ShireExpression.Normal }, onDecision = { id, decision -> approvals = approvals.map { item -> if (item.id == id) item.copy(status = decision) else item } expression = when (decision) { "Approved" -> ShireExpression.Happy "Rejected" -> ShireExpression.Concerned "Changes Requested" -> ShireExpression.Thinking else -> ShireExpression.Normal } message = when (decision) { "Approved" -> "Stage approved. Only that exact stage may proceed." "Rejected" -> "Plan rejected. No action will be taken." "Deferred" -> "Plan deferred. It remains inactive." "Changes Requested" -> "Changes requested. SHiRE must produce a revised plan." else -> "Approval status updated." } } ) } } @Composable private fun RealShireFace(expression: ShireExpression) { val transition = rememberInfiniteTransition(label = "real-shire-face") val breathing by transition.animateFloat( initialValue = 0.985f, targetValue = 1.018f, animationSpec = infiniteRepeatable( animation = tween(2400), repeatMode = RepeatMode.Reverse ), label = "breathing" ) val floating by transition.animateFloat( initialValue = -2f, targetValue = 3f, animationSpec = infiniteRepeatable( animation = tween(3000), repeatMode = RepeatMode.Reverse ), label = "floating" ) val drawable = when (expression) { ShireExpression.Normal -> R.drawable.shire_face_main ShireExpression.Happy -> R.drawable.shire_face_happy ShireExpression.Thinking -> R.drawable.shire_face_thinking ShireExpression.Sentinel -> R.drawable.shire_face_sentinel ShireExpression.Concerned -> R.drawable.shire_face_scared } Card( modifier = Modifier .size(252.dp) .offset(y = floating.dp) .scale(breathing), shape = CircleShape, colors = CardDefaults.cardColors(containerColor = ShireRaised), elevation = CardDefaults.cardElevation(defaultElevation = 12.dp) ) { Box( modifier = Modifier .fillMaxSize() .background(ShireRaised), contentAlignment = Alignment.Center ) { AnimatedContent( targetState = drawable, label = "shire-expression" ) { faceDrawable -> Image( painter = painterResource(faceDrawable), contentDescription = "Animated SHiRE face", modifier = Modifier .fillMaxSize() .padding(4.dp) .clip(CircleShape), contentScale = ContentScale.Crop ) } } } } @Composable private fun StatusPanel(pendingCount: Int) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 720.dp), colors = CardDefaults.cardColors(containerColor = ShireSurface), shape = RoundedCornerShape(20.dp) ) { FlowRow( modifier = Modifier.padding(15.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { StatusChip("SHiRE MINI", "Ready", ShireGreen) StatusChip("SENTINEL", "Protected", ShireGreen) StatusChip("ACADEMY", "Available", ShireBlue) StatusChip("APPROVALS", "$pendingCount pending", ShireAmber) } } } @Composable private fun StatusChip( title: String, value: String, valueColor: Color ) { Card( colors = CardDefaults.cardColors(containerColor = ShireRaised), shape = RoundedCornerShape(14.dp) ) { Column( modifier = Modifier.padding( horizontal = 13.dp, vertical = 9.dp ) ) { Text( text = title, fontSize = 10.sp, color = ShireMuted, fontWeight = FontWeight.Bold ) Text( text = value, fontSize = 14.sp, color = valueColor, fontWeight = FontWeight.SemiBold ) } } } @Composable private fun MainActions( pendingCount: Int, onLearnPrint: () -> Unit, onApprovals: () -> Unit, onShire: () -> Unit, onChatGpt: () -> Unit, onBoth: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 720.dp), colors = CardDefaults.cardColors(containerColor = ShireSurface), shape = RoundedCornerShape(20.dp) ) { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp) ) { Button( onClick = onLearnPrint, modifier = Modifier .fillMaxWidth() .height(58.dp), colors = ButtonDefaults.buttonColors( containerColor = ShirePurple, contentColor = Color.Black ), shape = RoundedCornerShape(16.dp) ) { Text( text = "LEARN & PRINT", fontWeight = FontWeight.Black, fontSize = 17.sp ) } Button( onClick = onApprovals, modifier = Modifier .fillMaxWidth() .height(52.dp), colors = ButtonDefaults.buttonColors( containerColor = ShireAmber, contentColor = Color.Black ), shape = RoundedCornerShape(16.dp) ) { Text( text = "APPROVAL CENTRE • $pendingCount", fontWeight = FontWeight.Black ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedButton( onClick = onShire, modifier = Modifier.weight(1f) ) { Text("SHiRE") } OutlinedButton( onClick = onChatGpt, modifier = Modifier.weight(1f) ) { Text("ChatGPT") } OutlinedButton( onClick = onBoth, modifier = Modifier.weight(1f) ) { Text("Both") } } } } } @Composable private fun RecommendedLearningCard( onPlan: () -> Unit, onDefer: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 720.dp), colors = CardDefaults.cardColors(containerColor = ShireSurface), shape = RoundedCornerShape(20.dp) ) { Column(modifier = Modifier.padding(17.dp)) { Text( text = "RECOMMENDED NEXT", fontSize = 12.sp, color = ShireMuted, fontWeight = FontWeight.Bold, letterSpacing = 1.3.sp ) Spacer(modifier = Modifier.height(7.dp)) Text( text = "Parametric phone support geometry", fontSize = 18.sp, fontWeight = FontWeight.Bold ) Spacer(modifier = Modifier.height(6.dp)) Text( text = "Strengthens DockForge and teaches reusable dimensional support geometry.", color = ShireMuted, lineHeight = 20.sp ) Spacer(modifier = Modifier.height(7.dp)) Text( text = "Expected benefit: Forge + income", color = ShireBlue, fontWeight = FontWeight.SemiBold ) Text( text = "Estimated effort: 45–90 minutes", color = ShireGreen ) Spacer(modifier = Modifier.height(11.dp)) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Button(onClick = onPlan) { Text("Add for Approval") } OutlinedButton(onClick = onDefer) { Text("Not Now") } } } } } @Composable private fun ExpressionTester( onNormal: () -> Unit, onHappy: () -> Unit, onThinking: () -> Unit, onSentinel: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 720.dp), colors = CardDefaults.cardColors(containerColor = ShireSurface), shape = RoundedCornerShape(18.dp) ) { Column(modifier = Modifier.padding(14.dp)) { Text( text = "FACE TEST", color = ShireMuted, fontSize = 11.sp, fontWeight = FontWeight.Bold ) Spacer(modifier = Modifier.height(7.dp)) FlowRow( horizontalArrangement = Arrangement.spacedBy(7.dp), verticalArrangement = Arrangement.spacedBy(7.dp) ) { OutlinedButton(onClick = onNormal) { Text("Normal") } OutlinedButton(onClick = onHappy) { Text("Happy") } OutlinedButton(onClick = onThinking) { Text("Thinking") } OutlinedButton(onClick = onSentinel) { Text("Sentinel") } } } } } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun LearnAndPrintDialog( onDismiss: () -> Unit, onCreatePlan: (String, String) -> Unit ) { val modes = listOf( "Learn Only", "Design Only", "Prepare to Print", "Learn + Design + Print" ) var request by remember { mutableStateOf("") } var selectedMode by remember { mutableStateOf("Learn + Design + Print") } var selectedPrinter by remember { mutableStateOf("Bambu P1S #1") } var selectedMaterial by remember { mutableStateOf("PLA") } AlertDialog( onDismissRequest = onDismiss, title = { Column { Text( text = "Learn & Print", fontSize = 24.sp, fontWeight = FontWeight.Black ) Text( text = "Create an approval-gated SHiRE mission.", color = ShireMuted, fontSize = 13.sp ) } }, text = { Column( modifier = Modifier .heightIn(max = 580.dp) .verticalScroll(rememberScrollState()) ) { OutlinedTextField( value = request, onValueChange = { if (it.length <= 1000) request = it }, modifier = Modifier .fillMaxWidth() .heightIn(min = 155.dp), label = { Text("Describe the mission") }, supportingText = { Text( text = "${request.length} / 1000", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End ) }, placeholder = { Text( "Example: Learn how to design a gothic controller holder, " + "show me the model and prepare it for black PLA." ) }, maxLines = 12 ) Spacer(modifier = Modifier.height(10.dp)) Text( text = "MISSION TYPE", color = ShireMuted, fontSize = 11.sp, fontWeight = FontWeight.Bold ) modes.forEach { mode -> Row( verticalAlignment = Alignment.CenterVertically ) { RadioButton( selected = selectedMode == mode, onClick = { selectedMode = mode } ) Text(mode) } } Text( text = "PRINTER", color = ShireMuted, fontSize = 11.sp, fontWeight = FontWeight.Bold ) FlowRow( horizontalArrangement = Arrangement.spacedBy(7.dp) ) { listOf( "Bambu P1S #1", "Bambu P1S #2", "Creality K2 Plus", "Decide Later" ).forEach { printer -> FilterChip( selected = selectedPrinter == printer, onClick = { selectedPrinter = printer }, label = { Text(printer) } ) } } Spacer(modifier = Modifier.height(8.dp)) Text( text = "MATERIAL", color = ShireMuted, fontSize = 11.sp, fontWeight = FontWeight.Bold ) FlowRow( horizontalArrangement = Arrangement.spacedBy(7.dp) ) { listOf( "PLA", "PETG", "TPU", "Decide Later" ).forEach { material -> FilterChip( selected = selectedMaterial == material, onClick = { selectedMaterial = material }, label = { Text(material) } ) } } Spacer(modifier = Modifier.height(9.dp)) Card( colors = CardDefaults.cardColors( containerColor = ShireRaised ) ) { Text( text = "Submitting creates a Safe Plan in the Approval Centre. " + "Learning, design, slicing and printing each remain separately locked.", modifier = Modifier.padding(12.dp), color = ShireGreen, fontSize = 12.sp, lineHeight = 17.sp ) } } }, confirmButton = { Button( enabled = request.isNotBlank(), onClick = { onCreatePlan( request.trim(), "$selectedMode • $selectedPrinter • $selectedMaterial" ) } ) { Text("Create Safe Plan") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } }, containerColor = ShireSurface ) } @Composable private fun ApprovalCentreDialog( approvals: List, onDismiss: () -> Unit, onDecision: (Int, String) -> Unit ) { var selectedId by remember { mutableIntStateOf( approvals.firstOrNull { it.status == "Pending" }?.id ?: approvals.firstOrNull()?.id ?: -1 ) } val selected = approvals.firstOrNull { it.id == selectedId } AlertDialog( onDismissRequest = onDismiss, title = { Column { Text( text = "Approval Centre", fontSize = 24.sp, fontWeight = FontWeight.Black ) Text( text = "${approvals.count { it.status == "Pending" }} pending", color = ShireAmber, fontSize = 13.sp ) } }, text = { Column( modifier = Modifier .heightIn(max = 610.dp) .verticalScroll(rememberScrollState()) ) { if (approvals.isEmpty()) { Text("No approval items.") } else { approvals.forEach { item -> FilterChip( selected = selectedId == item.id, onClick = { selectedId = item.id }, label = { Text("${item.stage}: ${item.title} — ${item.status}") }, modifier = Modifier.fillMaxWidth() ) } selected?.let { item -> Spacer(modifier = Modifier.height(12.dp)) ApprovalDetail("STAGE", item.stage, ShireAmber) ApprovalDetail("PURPOSE", item.purpose, ShireText) ApprovalDetail("AFFECTS", item.affects, ShireBlue) ApprovalDetail("RISK / SAFETY", item.risk, ShireRed) ApprovalDetail("ESTIMATE", item.estimate, ShireGreen) ApprovalDetail("ROLLBACK", item.reversible, ShireMuted) ApprovalDetail("STATUS", item.status, ShireAmber) if (item.status == "Pending") { Spacer(modifier = Modifier.height(10.dp)) Text( text = "Approval applies only to this exact stage. " + "It does not approve later stages.", color = ShireGreen, fontSize = 12.sp ) Spacer(modifier = Modifier.height(10.dp)) FlowRow( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { onDecision(item.id, "Approved") } ) { Text("Approve Stage") } OutlinedButton( onClick = { onDecision(item.id, "Changes Requested") } ) { Text("Request Changes") } OutlinedButton( onClick = { onDecision(item.id, "Deferred") } ) { Text("Defer") } Button( onClick = { onDecision(item.id, "Rejected") }, colors = ButtonDefaults.buttonColors( containerColor = ShireRed, contentColor = Color.Black ) ) { Text("Reject") } } } } } } }, confirmButton = { TextButton(onClick = onDismiss) { Text("Close") } }, containerColor = ShireSurface ) } @Composable private fun ApprovalDetail( label: String, value: String, valueColor: Color ) { Text( text = label, fontSize = 10.sp, fontWeight = FontWeight.Bold, color = ShireMuted, letterSpacing = 1.sp ) Text( text = value, color = valueColor, lineHeight = 19.sp ) Spacer(modifier = Modifier.height(9.dp)) }