Update:
- Logo - Logo Male, Female - Sedikit Perubahan UI/UX
72
app/src/main/java/com/example/tiptime/BmiLogic.kt
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package com.example.tiptime
|
||||||
|
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
// File ini berisi semua logika bisnis dan kalkulasi untuk aplikasi BMI.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data class untuk menampung semua hasil kalkulasi BMI.
|
||||||
|
*/
|
||||||
|
data class BmiFullResult(
|
||||||
|
val bmi: Double,
|
||||||
|
val category: String,
|
||||||
|
val healthyBmiRange: String = "18.5 - 25.0",
|
||||||
|
val healthyWeightRange: String,
|
||||||
|
val bmiPrime: Double,
|
||||||
|
val ponderalIndex: Double
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menghitung semua nilai terkait BMI berdasarkan berat, tinggi, dan sistem unit.
|
||||||
|
* @param weight Berat badan pengguna (dalam kg atau lbs).
|
||||||
|
* @param height Tinggi badan pengguna (dalam cm atau in).
|
||||||
|
* @param useMetric `true` jika menggunakan sistem Metrik, `false` jika USC.
|
||||||
|
* @return `BmiFullResult` jika input valid, atau `null` jika tidak.
|
||||||
|
*/
|
||||||
|
fun calculateBmi(weight: Double, height: Double, useMetric: Boolean): BmiFullResult? {
|
||||||
|
if (height <= 0 || weight <= 0) return null
|
||||||
|
|
||||||
|
val bmi = if (useMetric) {
|
||||||
|
val heightInMeters = height / 100
|
||||||
|
weight / heightInMeters.pow(2)
|
||||||
|
} else {
|
||||||
|
(weight * 703) / height.pow(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
val category = determineBmiCategory(bmi)
|
||||||
|
val heightInMeters = if (useMetric) height / 100 else height * 0.0254
|
||||||
|
val minHealthyWeight = 18.5 * heightInMeters.pow(2)
|
||||||
|
val maxHealthyWeight = 25.0 * heightInMeters.pow(2)
|
||||||
|
|
||||||
|
val healthyWeightRangeString = if (useMetric) {
|
||||||
|
String.format("%.1f kg - %.1f kg", minHealthyWeight, maxHealthyWeight)
|
||||||
|
} else {
|
||||||
|
val minHealthyWeightLbs = minHealthyWeight * 2.20462
|
||||||
|
val maxHealthyWeightLbs = maxHealthyWeight * 2.20462
|
||||||
|
String.format("%.1f lbs - %.1f lbs", minHealthyWeightLbs, maxHealthyWeightLbs)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bmiPrime = bmi / 25.0
|
||||||
|
val weightInKg = if (useMetric) weight else weight * 0.453592
|
||||||
|
val ponderalIndex = weightInKg / heightInMeters.pow(3)
|
||||||
|
|
||||||
|
return BmiFullResult(
|
||||||
|
bmi = bmi,
|
||||||
|
category = category,
|
||||||
|
healthyWeightRange = healthyWeightRangeString,
|
||||||
|
bmiPrime = bmiPrime,
|
||||||
|
ponderalIndex = ponderalIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menentukan kategori BMI berdasarkan nilainya.
|
||||||
|
*/
|
||||||
|
fun determineBmiCategory(bmi: Double): String {
|
||||||
|
return when {
|
||||||
|
bmi < 18.5 -> "Berat badan kurang"
|
||||||
|
bmi < 25 -> "Normal"
|
||||||
|
bmi < 30 -> "Berat badan lebih"
|
||||||
|
else -> "Obesitas"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,35 +9,39 @@ import androidx.activity.compose.setContent
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.DateRange
|
import androidx.compose.material.icons.filled.DateRange
|
||||||
import androidx.compose.material.icons.filled.LineWeight
|
import androidx.compose.material.icons.filled.MonitorWeight
|
||||||
import androidx.compose.material.icons.filled.Person
|
|
||||||
import androidx.compose.material.icons.filled.SwapVert
|
import androidx.compose.material.icons.filled.SwapVert
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.example.tiptime.ui.theme.TipTimeTheme
|
import com.example.tiptime.ui.theme.TipTimeTheme
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
// --- PERBAIKAN DI SINI: Menambahkan kembali import yang hilang ---
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@ -53,15 +57,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class BmiFullResult(
|
|
||||||
val bmi: Double,
|
|
||||||
val category: String,
|
|
||||||
val healthyBmiRange: String = "18.5 - 25.0",
|
|
||||||
val healthyWeightRange: String,
|
|
||||||
val bmiPrime: Double,
|
|
||||||
val ponderalIndex: Double
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BmiCalculatorScreen() {
|
fun BmiCalculatorScreen() {
|
||||||
var heightInput by remember { mutableStateOf("") }
|
var heightInput by remember { mutableStateOf("") }
|
||||||
@ -76,8 +71,6 @@ fun BmiCalculatorScreen() {
|
|||||||
heightInput = ""
|
heightInput = ""
|
||||||
weightInput = ""
|
weightInput = ""
|
||||||
ageInput = ""
|
ageInput = ""
|
||||||
selectedGender = "Laki-laki"
|
|
||||||
useMetricUnits = true
|
|
||||||
bmiResult = null
|
bmiResult = null
|
||||||
validationError = null
|
validationError = null
|
||||||
}
|
}
|
||||||
@ -89,15 +82,11 @@ fun BmiCalculatorScreen() {
|
|||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text("Kalkulator BMI", style = MaterialTheme.typography.headlineLarge, fontWeight = FontWeight.Bold)
|
Header()
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
Text("by Hadi Prakoso", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.primary)
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
|
|
||||||
AnimatedVisibility(visible = bmiResult != null) {
|
AnimatedVisibility(visible = bmiResult != null) {
|
||||||
bmiResult?.let { ResultDisplay(it) }
|
bmiResult?.let { ResultDisplay(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bmiResult != null) {
|
if (bmiResult != null) {
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
}
|
}
|
||||||
@ -115,7 +104,8 @@ fun BmiCalculatorScreen() {
|
|||||||
text = it,
|
text = it,
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,24 +117,11 @@ fun BmiCalculatorScreen() {
|
|||||||
val weight = weightInput.toDoubleOrNull()
|
val weight = weightInput.toDoubleOrNull()
|
||||||
val age = ageInput.toIntOrNull()
|
val age = ageInput.toIntOrNull()
|
||||||
|
|
||||||
// Input Validation
|
|
||||||
when {
|
when {
|
||||||
height == null || weight == null || age == null -> {
|
height == null || weight == null || age == null -> validationError = "Semua kolom harus diisi dengan angka."
|
||||||
validationError = "Semua kolom (Umur, Tinggi, Berat) harus diisi dengan angka."
|
age <= 0 || age > 120 -> validationError = "Umur tidak wajar."
|
||||||
bmiResult = null
|
(useMetricUnits && (height <= 50 || height > 270)) || (!useMetricUnits && (height <= 20 || height > 108)) -> validationError = "Tinggi badan tidak wajar."
|
||||||
}
|
(useMetricUnits && (weight <= 20 || weight > 500)) || (!useMetricUnits && (weight <= 45 || weight > 1100)) -> validationError = "Berat badan tidak wajar."
|
||||||
age <= 0 || age > 120 -> {
|
|
||||||
validationError = "Umur tidak wajar."
|
|
||||||
bmiResult = null
|
|
||||||
}
|
|
||||||
(useMetricUnits && (height <= 50 || height > 270)) || (!useMetricUnits && (height <= 20 || height > 108)) -> {
|
|
||||||
validationError = "Tinggi badan tidak wajar."
|
|
||||||
bmiResult = null
|
|
||||||
}
|
|
||||||
(useMetricUnits && (weight <= 20 || weight > 500)) || (!useMetricUnits && (weight <= 45 || weight > 1100)) -> {
|
|
||||||
validationError = "Berat badan tidak wajar."
|
|
||||||
bmiResult = null
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
validationError = null
|
validationError = null
|
||||||
bmiResult = calculateBmi(weight, height, useMetricUnits)
|
bmiResult = calculateBmi(weight, height, useMetricUnits)
|
||||||
@ -156,6 +133,21 @@ fun BmiCalculatorScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Header() {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.ic_launcher_playstore),
|
||||||
|
contentDescription = "Logo Aplikasi",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(80.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Text("Kalkulator BMI", style = MaterialTheme.typography.headlineLarge)
|
||||||
|
Text("by Hadi Prakoso", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.primary)
|
||||||
|
Spacer(Modifier.height(24.dp))
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ResultDisplay(result: BmiFullResult) {
|
fun ResultDisplay(result: BmiFullResult) {
|
||||||
Card(
|
Card(
|
||||||
@ -164,13 +156,12 @@ fun ResultDisplay(result: BmiFullResult) {
|
|||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(24.dp).fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(Modifier.padding(24.dp).fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Text("Hasil", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
Text("Hasil", style = MaterialTheme.typography.headlineSmall)
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
BmiGauge(bmi = result.bmi)
|
BmiGauge(bmi = result.bmi)
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
Text("BMI = ${String.format("%.1f", result.bmi)} kg/m² (${result.category})",
|
Text("BMI = ${String.format("%.1f", result.bmi)} kg/m² (${result.category})",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
@ -221,8 +212,13 @@ fun BmiGauge(bmi: Double) {
|
|||||||
drawCircle(surfaceColor, radius = 5.dp.toPx(), center = center)
|
drawCircle(surfaceColor, radius = 5.dp.toPx(), center = center)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(
|
||||||
Text(text = "BMI", style = MaterialTheme.typography.bodyLarge)
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Bottom,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(bottom = 50.dp)
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if (bmi > 0) String.format(Locale.getDefault(), "%.1f", bmi) else "-",
|
text = if (bmi > 0) String.format(Locale.getDefault(), "%.1f", bmi) else "-",
|
||||||
style = MaterialTheme.typography.displayMedium.copy(fontWeight = FontWeight.Bold, color = onSurfaceColor)
|
style = MaterialTheme.typography.displayMedium.copy(fontWeight = FontWeight.Bold, color = onSurfaceColor)
|
||||||
@ -252,12 +248,18 @@ fun InputPanel(
|
|||||||
val heightLabel = if (useMetric) "Tinggi (cm)" else "Tinggi (in)"
|
val heightLabel = if (useMetric) "Tinggi (cm)" else "Tinggi (in)"
|
||||||
val weightLabel = if (useMetric) "Berat (kg)" else "Berat (lbs)"
|
val weightLabel = if (useMetric) "Berat (kg)" else "Berat (lbs)"
|
||||||
|
|
||||||
Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
Card(
|
||||||
UnitToggle(useMetric = useMetric, onUnitChange = onUnitChange)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
GenderSelector(selectedGender, onOptionSelected = { onGenderSelect(it) })
|
shape = RoundedCornerShape(16.dp),
|
||||||
BmiTextField(label = "Umur", value = ageInput, onValueChange = onAgeChange, icon = Icons.Default.DateRange)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f))
|
||||||
BmiTextField(label = heightLabel, value = heightInput, onValueChange = onHeightChange, icon = Icons.Default.SwapVert)
|
) {
|
||||||
BmiTextField(label = weightLabel, value = weightInput, onValueChange = onWeightChange, icon = Icons.Default.LineWeight)
|
Column(Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
|
UnitToggle(useMetric = useMetric, onUnitChange = onUnitChange)
|
||||||
|
GenderSelector(selectedGender, onOptionSelected = { onGenderSelect(it) })
|
||||||
|
BmiTextField(label = "Umur", value = ageInput, onValueChange = onAgeChange, icon = Icons.Default.DateRange)
|
||||||
|
BmiTextField(label = heightLabel, value = heightInput, onValueChange = onHeightChange, icon = Icons.Default.SwapVert)
|
||||||
|
BmiTextField(label = weightLabel, value = weightInput, onValueChange = onWeightChange, icon = Icons.Default.MonitorWeight)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +290,8 @@ fun GenderSelector(selectedOption: String, onOptionSelected: (String) -> Unit) {
|
|||||||
val options = listOf("Laki-laki", "Perempuan")
|
val options = listOf("Laki-laki", "Perempuan")
|
||||||
options.forEach { gender ->
|
options.forEach { gender ->
|
||||||
val isSelected = selectedOption == gender
|
val isSelected = selectedOption == gender
|
||||||
|
val iconRes = if (gender == "Laki-laki") R.drawable.ic_male else R.drawable.ic_female
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { onOptionSelected(gender) },
|
onClick = { onOptionSelected(gender) },
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
@ -297,7 +301,11 @@ fun GenderSelector(selectedOption: String, onOptionSelected: (String) -> Unit) {
|
|||||||
),
|
),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Default.Person, contentDescription = gender, modifier = Modifier.size(18.dp))
|
Icon(
|
||||||
|
painter = painterResource(id = iconRes),
|
||||||
|
contentDescription = gender,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
Text(gender)
|
Text(gender)
|
||||||
}
|
}
|
||||||
@ -318,52 +326,6 @@ fun BmiTextField(label: String, value: String, onValueChange: (String) -> Unit,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Calculation Logic ---
|
|
||||||
fun calculateBmi(weight: Double, height: Double, useMetric: Boolean): BmiFullResult? {
|
|
||||||
if (height <= 0 || weight <= 0) return null
|
|
||||||
|
|
||||||
val bmi = if (useMetric) {
|
|
||||||
val heightInMeters = height / 100
|
|
||||||
weight / heightInMeters.pow(2)
|
|
||||||
} else {
|
|
||||||
(weight * 703) / height.pow(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
val category = determineBmiCategory(bmi)
|
|
||||||
val heightInMeters = if (useMetric) height / 100 else height * 0.0254
|
|
||||||
val minHealthyWeight = 18.5 * heightInMeters.pow(2)
|
|
||||||
val maxHealthyWeight = 25.0 * heightInMeters.pow(2)
|
|
||||||
|
|
||||||
val healthyWeightRangeString = if (useMetric) {
|
|
||||||
String.format("%.1f kg - %.1f kg", minHealthyWeight, maxHealthyWeight)
|
|
||||||
} else {
|
|
||||||
val minHealthyWeightLbs = minHealthyWeight * 2.20462
|
|
||||||
val maxHealthyWeightLbs = maxHealthyWeight * 2.20462
|
|
||||||
String.format("%.1f lbs - %.1f lbs", minHealthyWeightLbs, maxHealthyWeightLbs)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bmiPrime = bmi / 25.0
|
|
||||||
val weightInKg = if (useMetric) weight else weight * 0.453592
|
|
||||||
val ponderalIndex = weightInKg / heightInMeters.pow(3)
|
|
||||||
|
|
||||||
return BmiFullResult(
|
|
||||||
bmi = bmi,
|
|
||||||
category = category,
|
|
||||||
healthyWeightRange = healthyWeightRangeString,
|
|
||||||
bmiPrime = bmiPrime,
|
|
||||||
ponderalIndex = ponderalIndex
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun determineBmiCategory(bmi: Double): String {
|
|
||||||
return when {
|
|
||||||
bmi < 18.5 -> "Berat badan kurang"
|
|
||||||
bmi < 25 -> "Normal"
|
|
||||||
bmi < 30 -> "Berat badan lebih"
|
|
||||||
else -> "Obesitas"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 360, heightDp = 800)
|
@Preview(showBackground = true, widthDp = 360, heightDp = 800)
|
||||||
@Composable
|
@Composable
|
||||||
fun BmiCalculatorScreenPreview() {
|
fun BmiCalculatorScreenPreview() {
|
||||||
|
|||||||
@ -5,32 +5,42 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
// Set of Material typography styles to start with
|
// Mengatur Typography default untuk menggunakan font Poppins
|
||||||
val Typography = Typography(
|
val Typography = Typography(
|
||||||
// Override default text styles to use Poppins
|
headlineLarge = TextStyle(
|
||||||
headlineMedium = TextStyle(
|
|
||||||
fontFamily = Poppins,
|
fontFamily = Poppins,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 28.sp
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 40.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
titleMedium = TextStyle(
|
titleLarge = TextStyle(
|
||||||
fontFamily = Poppins,
|
fontFamily = Poppins,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 16.sp
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
bodyLarge = TextStyle(
|
bodyLarge = TextStyle(
|
||||||
fontFamily = Poppins,
|
fontFamily = Poppins,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 24.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
),
|
),
|
||||||
bodyMedium = TextStyle(
|
bodyMedium = TextStyle(
|
||||||
fontFamily = Poppins,
|
fontFamily = Poppins,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 14.sp
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
letterSpacing = 0.25.sp
|
||||||
),
|
),
|
||||||
displayMedium = TextStyle(
|
labelMedium = TextStyle(
|
||||||
fontFamily = Poppins,
|
fontFamily = Poppins,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 57.sp
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
)
|
)
|
||||||
|
/* Anda bisa menambahkan style lain di sini jika diperlukan */
|
||||||
)
|
)
|
||||||
|
|||||||
13
app/src/main/res/drawable/ic_female.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M9,18H10M12,18H15M12,13V21M12,13C14.761,13 17,10.761 17,8M12,13C9.239,13 7,10.761 7,8C7,5.239 9.239,3 12,3C13.126,3 14.164,3.372 15,4"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/drawable/ic_launcher_playstore.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
13
app/src/main/res/drawable/ic_male.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M17,16C17,18.761 14.761,21 12,21C9.239,21 7,18.761 7,16C7,13.239 9.239,11 12,11M12,11C13.126,11 14.164,11.372 15,12M12,11V3L16,7M10,5L8,7"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
@ -1,22 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2023 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
</adaptive-icon>
|
||||||
</adaptive-icon>
|
|
||||||
@ -1,22 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2023 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
</adaptive-icon>
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 8.3 KiB |
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#1F7A6F</color>
|
||||||
|
</resources>
|
||||||