This commit is contained in:
SKILLISSUE1 2025-11-06 23:39:10 +07:00
parent 099c35f19a
commit 2677e3f16c
26 changed files with 113 additions and 51 deletions

View File

@ -1,7 +1,18 @@
Kalkulator BMI
===============
Langkah-langkah:
Silahkan kembangkan aplikasi ini untuk melakukan perhitungan BMI
1. Clone Repository Project
Saya meng-clone repository project dari GitHub menggunakan Android Studio untuk dijadikan dasar pengerjaan tugas.
2. Modifikasi Tampilan Aplikasi
Saya meminta bantuan ChatGPT dan DeepSeek untuk memberikan saran tampilan aplikasi agar lebih menarik. Setelah itu, saya menyesuaikan tampilan sesuai arahan yang diberikan.
3. Mengubah Icon Aplikasi (APK)
Saya mengganti icon aplikasi melalui fitur Image Asset di Android Studio dengan icon baru sesuai arahan dari ChatGPT.
4. Review dan Uji Coba
Setelah perubahan selesai, saya menjalankan aplikasi untuk memastikan tampilan dan icon sudah berubah dengan baik.
Petunjuk lebih detil dapat dibaca di
https://docs.google.com/document/d/1iGiC0Bg3Bdcd2Maq45TYkCDUkZ5Ql51E/edit?rtpof=true

View File

@ -20,9 +20,9 @@
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/app_icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/app_icon_round"
android:supportsRtl="true"
android:theme="@style/Theme.TipTime"
tools:targetApi="33">

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -54,8 +54,9 @@ 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.unit.dp
import androidx.compose.ui.unit.sp
import com.example.tiptime.ui.theme.TipTimeTheme
import java.text.NumberFormat
import java.text.DecimalFormat
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -75,14 +76,16 @@ class MainActivity : ComponentActivity() {
@Composable
fun TipTimeLayout() {
var amountInput by remember { mutableStateOf("") }
var tipInput by remember { mutableStateOf("") }
var roundUp by remember { mutableStateOf(false) }
var heightInput by remember { mutableStateOf("") }
var weightInput by remember { mutableStateOf("") }
var useUSC by remember { mutableStateOf(false) }
val BmiHeight = amountInput.toDoubleOrNull() ?: 0.0
val BmiWeight = tipInput.toDoubleOrNull() ?: 0.0
val bmi = calculateBMI(BmiHeight, BmiWeight, roundUp)
val category = calculateBMICategory(BmiHeight, BmiWeight, roundUp)
val height = heightInput.toDoubleOrNull() ?: 0.0
val weight = weightInput.toDoubleOrNull() ?: 0.0
val bmi = calculateBMI(height, weight, useUSC)
val category = calculateBMICategory(bmi)
val categoryColor = getCategoryColor(category)
Column(
modifier = Modifier
@ -94,11 +97,14 @@ fun TipTimeLayout() {
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(R.string.calculate_tip),
text = "Calculate BMI",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(bottom = 16.dp, top = 40.dp)
.align(alignment = Alignment.Start)
)
EditNumberField(
label = R.string.height,
leadingIcon = R.drawable.number,
@ -106,10 +112,13 @@ fun TipTimeLayout() {
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
value = amountInput,
onValueChanged = { amountInput = it },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(),
value = heightInput,
onValueChanged = { heightInput = it },
modifier = Modifier
.padding(bottom = 32.dp)
.fillMaxWidth(),
)
EditNumberField(
label = R.string.weight,
leadingIcon = R.drawable.number,
@ -117,22 +126,30 @@ fun TipTimeLayout() {
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
value = tipInput,
onValueChanged = { tipInput = it },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(),
value = weightInput,
onValueChanged = { weightInput = it },
modifier = Modifier
.padding(bottom = 32.dp)
.fillMaxWidth(),
)
RoundTheTipRow(
roundUp = roundUp,
onRoundUpChanged = { roundUp = it },
RoundTheUnitRow(
useUSC = useUSC,
onUnitChanged = { useUSC = it },
modifier = Modifier.padding(bottom = 32.dp)
)
Text(
text = stringResource(R.string.bmi_calculation, bmi),
style = MaterialTheme.typography.displaySmall
text = "BMI Anda: $bmi",
style = MaterialTheme.typography.displaySmall,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = stringResource(R.string.bmi_category, category),
style = MaterialTheme.typography.displaySmall
text = "Kategori: $category",
fontSize = 20.sp,
color = categoryColor,
modifier = Modifier.padding(top = 8.dp)
)
Spacer(modifier = Modifier.height(150.dp))
@ -160,51 +177,75 @@ fun EditNumberField(
}
@Composable
fun RoundTheTipRow(
roundUp: Boolean,
onRoundUpChanged: (Boolean) -> Unit,
fun RoundTheUnitRow(
useUSC: Boolean,
onUnitChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = stringResource(R.string.use_usc))
Text(
text = "Gunakan Unit (Metric/USC)",
color = MaterialTheme.colorScheme.onSurface
)
Switch(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
checked = roundUp,
onCheckedChange = onRoundUpChanged
checked = useUSC,
onCheckedChange = onUnitChanged
)
}
}
/**
* 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
* Menghitung BMI berdasarkan rumus yang digunakan
*/
internal fun calculateBMI(height: Double, weight: Double, useUSC: Boolean): String {
if (height == 0.0 || weight == 0.0) return "0.0"
private fun calculateBMICategory(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String {
var bmi = BmiWeight / 100 * BmiHeight
if (roundUp) {
bmi = kotlin.math.ceil(bmi)
val bmi = if (useUSC) {
// Rumus USC: 703 * (berat (lbs) / (tinggi (in)^2))
703 * (weight / (height * height))
} else {
// Rumus Metric: berat (kg) / (tinggi (m)^2)
val heightInMeter = height / 100
weight / (heightInMeter * heightInMeter)
}
return NumberFormat.getNumberInstance().format(bmi)
return DecimalFormat("#.0").format(bmi)
}
/**
* Menentukan kategori BMI
*/
internal fun calculateBMICategory(bmiString: String): String {
val bmi = bmiString.toDoubleOrNull() ?: 0.0
return when {
bmi < 18.5 -> "Underweight"
bmi < 25.0 -> "Normal weight"
bmi < 30.0 -> "Overweight"
bmi >= 30.0 -> "Obese"
else -> "Tidak diketahui"
}
}
/**
* Memberikan warna berdasarkan kategori BMI - menggunakan warna dari tema
*/
@Composable
fun getCategoryColor(category: String): androidx.compose.ui.graphics.Color {
return when (category) {
"Underweight" -> androidx.compose.ui.graphics.Color(0xFF2196F3) // Biru
"Normal weight" -> androidx.compose.ui.graphics.Color(0xFF4CAF50) // Hijau
"Overweight" -> androidx.compose.ui.graphics.Color(0xFFFF9800) // Orange
"Obese" -> androidx.compose.ui.graphics.Color(0xFFF44336) // Merah
else -> MaterialTheme.colorScheme.onSurface
}
}
@Preview(showBackground = true)
@Composable
fun TipTimeLayoutPreview() {

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/app_icon_background"/>
<foreground android:drawable="@mipmap/app_icon_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/app_icon_background"/>
<foreground android:drawable="@mipmap/app_icon_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB