diff --git a/app/src/main/java/com/example/tiptime/MainActivity.kt b/app/src/main/java/com/example/tiptime/MainActivity.kt index d0fdd80..2ffbd74 100644 --- a/app/src/main/java/com/example/tiptime/MainActivity.kt +++ b/app/src/main/java/com/example/tiptime/MainActivity.kt @@ -1,72 +1,31 @@ -/* - * 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. - */ package com.example.tiptime import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Arrangement -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.safeDrawingPadding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -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.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview +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 import com.example.tiptime.ui.theme.TipTimeTheme -import java.text.NumberFormat class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { TipTimeTheme { - Surface( - modifier = Modifier.fillMaxSize(), - ) { - TipTimeLayout() + Surface(modifier = Modifier.fillMaxSize(), color = Color(0xFFF4F4F4)) { + BMICalculatorApp() } } } @@ -74,141 +33,187 @@ class MainActivity : ComponentActivity() { } @Composable -fun TipTimeLayout() { - var amountInput by remember { mutableStateOf("") } - var tipInput by remember { mutableStateOf("") } - var roundUp by remember { mutableStateOf(false) } +fun BMICalculatorApp() { + var showSplash by remember { mutableStateOf(true) } - val BmiHeight = amountInput.toDoubleOrNull() ?: 0.0 - val BmiWeight = tipInput.toDoubleOrNull() ?: 0.0 - val bmi = calculateBMI(BmiHeight, BmiWeight, roundUp) - val category = calculateBMICategory(BmiHeight, BmiWeight, roundUp) + if (showSplash) { + SplashScreen(onStartClick = { showSplash = false }) + } else { + BMIScreen() + } +} + +@Composable +fun SplashScreen(onStartClick: () -> Unit) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFF2196F3)), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + // kalau logomu putih, ubah background jadi putih biar kelihatan + Surface( + color = Color.White, + shape = RoundedCornerShape(100.dp), + modifier = Modifier.size(150.dp) + ) { + Image( + painter = painterResource(id = R.drawable.logo), + contentDescription = "Logo", + modifier = Modifier + .fillMaxSize() + .padding(20.dp) + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "BMI Checker", + fontSize = 34.sp, + color = Color.White, + fontWeight = FontWeight.Bold + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Button( + onClick = onStartClick, + colors = ButtonDefaults.buttonColors(containerColor = Color.White), + shape = RoundedCornerShape(12.dp) + ) { + Text("Get Started", color = Color(0xFF2196F3), fontSize = 18.sp) + } + } + } +} + +@Composable +fun BMIScreen() { + var height by remember { mutableStateOf("") } + var weight by remember { mutableStateOf("") } + var bmi by remember { mutableStateOf(null) } + var selectedUnit by remember { mutableStateOf("Metric") } + + val unitOptions = listOf("Metric (kg/cm)", "US (lbs/inch)") Column( modifier = Modifier - .statusBarsPadding() - .padding(horizontal = 40.dp) - .verticalScroll(rememberScrollState()) - .safeDrawingPadding(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + .fillMaxSize() + .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { Text( - text = stringResource(R.string.calculate_tip), - modifier = Modifier - .padding(bottom = 16.dp, top = 40.dp) - .align(alignment = Alignment.Start) + text = "BMI Calculator", + fontSize = 30.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF2196F3) ) - EditNumberField( - label = R.string.height, - leadingIcon = R.drawable.number, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next - ), - value = amountInput, - onValueChanged = { amountInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), + + Spacer(Modifier.height(24.dp)) + + // Pilihan Metric / US + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + unitOptions.forEach { option -> + val isSelected = selectedUnit == option + Button( + onClick = { selectedUnit = option }, + colors = ButtonDefaults.buttonColors( + containerColor = if (isSelected) Color(0xFF2196F3) else Color.LightGray + ), + shape = RoundedCornerShape(50.dp), + modifier = Modifier + .padding(horizontal = 6.dp) + .height(45.dp) + ) { + Text( + option, + color = if (isSelected) Color.White else Color.Black, + fontSize = 13.sp + ) + } + } + } + + Spacer(Modifier.height(24.dp)) + + OutlinedTextField( + value = height, + onValueChange = { if (it.all { c -> c.isDigit() || c == '.' }) height = it }, + label = { Text("Tinggi Badan (${if (selectedUnit.startsWith("Metric")) "cm" else "inch"})") }, + modifier = Modifier.fillMaxWidth() ) - EditNumberField( - label = R.string.weight, - leadingIcon = R.drawable.number, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done - ), - value = tipInput, - onValueChanged = { tipInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), + + Spacer(Modifier.height(12.dp)) + + OutlinedTextField( + value = weight, + onValueChange = { if (it.all { c -> c.isDigit() || c == '.' }) weight = it }, + label = { Text("Berat Badan (${if (selectedUnit.startsWith("Metric")) "kg" else "lbs"})") }, + modifier = Modifier.fillMaxWidth() ) - RoundTheTipRow( - roundUp = roundUp, - onRoundUpChanged = { roundUp = it }, - modifier = Modifier.padding(bottom = 32.dp) + + Spacer(Modifier.height(20.dp)) + + Button( + onClick = { + val h = height.toDoubleOrNull() + val w = weight.toDoubleOrNull() + if (h != null && w != null && h > 0) { + bmi = if (selectedUnit.startsWith("Metric")) { + w / ((h / 100) * (h / 100)) + } else { + 703 * (w / (h * h)) + } + } + }, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF2196F3)) + ) { + Text("Hitung BMI", fontSize = 18.sp, color = Color.White) + } + + Spacer(Modifier.height(30.dp)) + + bmi?.let { + HasilBMI(bmi = it) + } + } +} + +@Composable +fun HasilBMI(bmi: Double) { + val (category, imageRes) = when { + bmi < 18.5 -> "Underweight" to R.drawable.stickman_kurus + bmi < 25 -> "Normal" to R.drawable.stickman_normal + bmi < 30 -> "Overweight" to R.drawable.stickman_overweight + else -> "Obesity" to R.drawable.stickman_obesitas + } + + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = String.format("BMI Anda: %.1f", bmi), + fontSize = 22.sp, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.Center ) Text( - text = stringResource(R.string.bmi_calculation, bmi), - style = MaterialTheme.typography.displaySmall + text = "Kategori: $category", + fontSize = 20.sp, + color = Color.Gray, + textAlign = TextAlign.Center ) - Text( - text = stringResource(R.string.bmi_category, category), - style = MaterialTheme.typography.displaySmall - ) - - Spacer(modifier = Modifier.height(150.dp)) - } -} - -@Composable -fun EditNumberField( - @StringRes label: Int, - @DrawableRes leadingIcon: Int, - keyboardOptions: KeyboardOptions, - value: String, - onValueChanged: (String) -> Unit, - modifier: Modifier = Modifier -) { - TextField( - value = value, - singleLine = true, - leadingIcon = { Icon(painter = painterResource(id = leadingIcon), null) }, - modifier = modifier, - onValueChange = onValueChanged, - label = { Text(stringResource(label)) }, - keyboardOptions = keyboardOptions - ) -} - -@Composable -fun RoundTheTipRow( - roundUp: Boolean, - onRoundUpChanged: (Boolean) -> Unit, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Text(text = stringResource(R.string.use_usc)) - Switch( + Spacer(Modifier.height(16.dp)) + Image( + painter = painterResource(id = imageRes), + contentDescription = category, modifier = Modifier - .fillMaxWidth() - .wrapContentWidth(Alignment.End), - checked = roundUp, - onCheckedChange = onRoundUpChanged + .fillMaxWidth(0.7f) + .aspectRatio(1f) ) } } - -/** - * Calculates the BMI - * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini - */ -private fun calculateBMI(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { - var bmi = BmiWeight / 100 * BmiHeight - if (roundUp) { - bmi = kotlin.math.ceil(bmi) - } - return NumberFormat.getNumberInstance().format(bmi) -} -/** - * Calculates the BMI Category - * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini - */ - -private fun calculateBMICategory(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { - var bmi = BmiWeight / 100 * BmiHeight - if (roundUp) { - bmi = kotlin.math.ceil(bmi) - } - return NumberFormat.getNumberInstance().format(bmi) -} -@Preview(showBackground = true) -@Composable -fun TipTimeLayoutPreview() { - TipTimeTheme { - TipTimeLayout() - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index a1fcc85..ca3826a 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,185 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 264eaf4..c4a603d 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,22 +1,5 @@ - - - - - - + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 264eaf4..c4a603d 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,22 +1,5 @@ - - - - - - + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78..2d7fb13 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d..ebe58c9 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d6..6f11975 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 62b611d..63b65fb 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a307..777cd84 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a695..d67c641 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77..0f38145 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 9287f50..ff5ffc0 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d642..aae2dec 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 9126ae3..5893172 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ