UTS
13
README.md
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
BIN
app/src/main/app_icon-playstore.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
@ -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() {
|
||||
|
||||
5
app/src/main/res/mipmap-anydpi-v26/app_icon.xml
Normal 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>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/app_icon_round.xml
Normal 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>
|
||||
BIN
app/src/main/res/mipmap-hdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-hdpi/app_icon_background.webp
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/app_icon_foreground.webp
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/res/mipmap-mdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/app_icon_background.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/app_icon_foreground.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/res/mipmap-xhdpi/app_icon_background.webp
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/mipmap-xhdpi/app_icon_foreground.webp
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/mipmap-xhdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/app_icon_background.webp
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/app_icon_foreground.webp
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon_background.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon_foreground.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |