Skip to content

Instantly share code, notes, and snippets.

@parishsu
Created March 3, 2025 21:52
Show Gist options
  • Save parishsu/f4f0ceb1c9a80eadba58adc34361f71c to your computer and use it in GitHub Desktop.
Save parishsu/f4f0ceb1c9a80eadba58adc34361f71c to your computer and use it in GitHub Desktop.
package com.example.multimodal
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Backspace
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import java.text.DecimalFormat
/**
* Composable function to create a calculator screen.
*
* This screen displays a basic calculator layout with number buttons,
* operation buttons, and a display area.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CalculatorScreen() {
var displayValue by remember { mutableStateOf("") } // Current value displayed
var displayOperation by remember { mutableStateOf("") } // Current operation displayed
var previousValue by remember { mutableStateOf("") } // Previous value displayed
var lastOperation by remember { mutableStateOf("") }
Scaffold(
// Top app bar
topBar = {
TopAppBar(
title = {
Text(
text = "DEG",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
},
actions = {
IconButton(onClick = { /*TODO: Open more menu*/ }) {
Icon(
imageVector = Icons.Filled.MoreVert,
contentDescription = "More menu",
tint = MaterialTheme.colorScheme.primary
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
)
)
},
modifier = Modifier.fillMaxSize()
) { innerPadding ->
Surface(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.background(MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.End
) {
// Display area for results
Box(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.padding(16.dp),
contentAlignment = Alignment.BottomEnd
) {
Text(
text = displayValue.ifEmpty { "0" },
fontSize = 48.sp,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.End,
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.padding(16.dp),
contentAlignment = Alignment.BottomEnd
) {
Text(
text = displayOperation,
fontSize = 24.sp,
color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.End,
)
}
Divider(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colorScheme.outline
)
Spacer(modifier = Modifier.height(16.dp))
// Calculator buttons
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// Row 1
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
CalculatorButton(
text = "AC",
buttonColor = MaterialTheme.colorScheme.surfaceVariant
) {
displayValue = ""
displayOperation = ""
previousValue = ""
lastOperation = ""
}
CalculatorButton(
text = "+/-",
buttonColor = MaterialTheme.colorScheme.surfaceVariant
) {
if (displayValue.isNotEmpty()) {
displayValue = if (displayValue.startsWith("-")) {
displayValue.removePrefix("-")
} else {
"-$displayValue"
}
}
}
CalculatorButton(
text = "%",
buttonColor = MaterialTheme.colorScheme.surfaceVariant
) {
if (displayValue.isNotEmpty()) {
previousValue = displayValue
displayValue = ""
lastOperation = "%"
displayOperation = "$previousValue %"
}
}
CalculatorButton(
text = "÷",
buttonColor = MaterialTheme.colorScheme.primaryContainer,
textColor = MaterialTheme.colorScheme.onPrimaryContainer
) {
if (displayValue.isNotEmpty()) {
previousValue = displayValue
displayValue = ""
lastOperation = "÷"
displayOperation = "$previousValue ÷"
}
}
}
// Row 2
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
CalculatorButton(text = "7") {
updateDisplayValue(
"7",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "8") {
updateDisplayValue(
"8",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "9") {
updateDisplayValue(
"9",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(
text = "x",
buttonColor = MaterialTheme.colorScheme.primaryContainer,
textColor = MaterialTheme.colorScheme.onPrimaryContainer
) {
if (displayValue.isNotEmpty()) {
previousValue = displayValue
displayValue = ""
lastOperation = "x"
displayOperation = "$previousValue x"
}
}
}
// Row 3
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
CalculatorButton(text = "4") {
updateDisplayValue(
"4",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "5") {
updateDisplayValue(
"5",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "6") {
updateDisplayValue(
"6",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(
text = "-",
buttonColor = MaterialTheme.colorScheme.primaryContainer,
textColor = MaterialTheme.colorScheme.onPrimaryContainer
) {
if (displayValue.isNotEmpty()) {
previousValue = displayValue
displayValue = ""
lastOperation = "-"
displayOperation = "$previousValue -"
}
}
}
// Row 4
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
CalculatorButton(text = "1") {
updateDisplayValue(
"1",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "2") {
updateDisplayValue(
"2",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = "3") {
updateDisplayValue(
"3",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(
text = "+",
buttonColor = MaterialTheme.colorScheme.primaryContainer,
textColor = MaterialTheme.colorScheme.onPrimaryContainer
) {
if (displayValue.isNotEmpty()) {
previousValue = displayValue
displayValue = ""
lastOperation = "+"
displayOperation = "$previousValue +"
}
}
}
// Row 5
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
CalculatorButton(text = "0") {
updateDisplayValue(
"0",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(text = ".") {
updateDisplayValue(
".",
displayValue,
setDisplayValue = { displayValue = it })
}
CalculatorButton(
icon = Icons.Filled.Backspace,
iconDescription = "backspace",
buttonColor = MaterialTheme.colorScheme.surfaceVariant
) {
if (displayValue.isNotEmpty()) {
displayValue = displayValue.dropLast(1)
}
}
CalculatorButton(
text = "=",
buttonColor = MaterialTheme.colorScheme.tertiary,
textColor = MaterialTheme.colorScheme.onTertiary
) {
if (displayValue.isNotEmpty() && previousValue.isNotEmpty()) {
displayValue = calculateResult(
previousValue,
displayValue,
lastOperation
).toString()
}
displayOperation = ""
lastOperation = ""
previousValue = ""
}
}
}
}
}
}
}
/**
* Composable function to create a calculator button.
*
* This function creates a circular button with a text or icon.
*
* @param text The text to display on the button (optional).
* @param icon The icon to display on the button (optional).
* @param iconDescription The description for the icon (optional).
* @param onClick Callback function to be executed when the button is clicked.
*/
@Composable
fun CalculatorButton(
text: String? = null,
icon: ImageVector? = null,
iconDescription: String? = null,
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
buttonColor: Color = MaterialTheme.colorScheme.surfaceVariant,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.background(buttonColor)
.border(1.dp, MaterialTheme.colorScheme.outline, CircleShape)
.clickable { onClick() },
contentAlignment = Alignment.Center
) {
if (text != null) {
Text(
text = text,
fontSize = 24.sp,
color = textColor,
fontWeight = FontWeight.Bold
)
} else if (icon != null) {
Icon(
imageVector = icon,
contentDescription = iconDescription,
tint = textColor,
modifier = Modifier.size(32.dp)
)
}
}
}
/**
* Function to update the display value.
*
* @param newValue The new value to append to the display.
*/
fun updateDisplayValue(newValue: String, displayValue: String, setDisplayValue: (String) -> Unit) {
// Check if the value is "." and if already exists in the number
if (newValue == "." && displayValue.contains(".")) {
return
}
setDisplayValue(displayValue + newValue)
}
fun calculateResult(previousValue: String, displayValue: String, lastOperation: String): Double {
val num1 = previousValue.toDoubleOrNull() ?: 0.0
val num2 = displayValue.toDoubleOrNull() ?: 0.0
val decimalFormat = DecimalFormat("#.#######")
return when (lastOperation) {
"+" -> decimalFormat.format(num1 + num2).toDouble()
"-" -> decimalFormat.format(num1 - num2).toDouble()
"x" -> decimalFormat.format(num1 * num2).toDouble()
"÷" -> {
if (num2 == 0.0) {
Log.e("Calculator", "Error: Division by zero")
0.0
} else {
decimalFormat.format(num1 / num2).toDouble()
}
}
"%" -> decimalFormat.format(num1 % num2).toDouble()
else -> 0.0
}
}
/**
* Preview function to display CalculatorScreen in the IDE.
*/
@Preview(showBackground = true)
@Composable
fun CalculatorScreenPreview() {
MaterialTheme {
CalculatorScreen()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment