commit a7f48dd36a0211bb1b1895bec533701835ee0d42 Author: nuryuda Date: Fri Nov 7 20:18:33 2025 +0700 first commit diff --git a/.github/ISSUE_TEMPLATE/calculate-a-custom-tip-issue-template.md b/.github/ISSUE_TEMPLATE/calculate-a-custom-tip-issue-template.md new file mode 100644 index 0000000..61186a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/calculate-a-custom-tip-issue-template.md @@ -0,0 +1,32 @@ +--- +name: Calculate a custom tip issue template +about: 'Issue template for Calculate a custom tip ' +title: 'Calculate a custom tip: Android Basics in Compose ' +labels: '' +assignees: '' + +--- + +**URL of codelab** + + +**In which task and step of the codelab can this issue be found?** + + +**Describe the problem** + + + + +**Steps to reproduce?** +1. Go to... +2. Click on... +3. See error... + +**Versions** +_Android Studio version:_ +_API version of the emulator:_ + + +**Additional information** +_Include screenshots if they would be useful in clarifying the problem._ diff --git a/.github/ISSUE_TEMPLATE/intro-to-state-issue-template.md b/.github/ISSUE_TEMPLATE/intro-to-state-issue-template.md new file mode 100644 index 0000000..2a148e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/intro-to-state-issue-template.md @@ -0,0 +1,32 @@ +--- +name: Intro to state issue template +about: 'Issue template for Intro to state in Compose ' +title: 'Intro to state in Compose: Android Basics in Compose' +labels: '' +assignees: '' + +--- + +**URL of codelab** + + +**In which task and step of the codelab can this issue be found?** + + +**Describe the problem** + + + + +**Steps to reproduce?** +1. Go to... +2. Click on... +3. See error... + +**Versions** +_Android Studio version:_ +_API version of the emulator:_ + + +**Additional information** +_Include screenshots if they would be useful in clarifying the problem._ diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..be8b65e --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>android/.github:renovate-config" + ], + "baseBranches": [ + "main", + "starter", + "state" + ] +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..fdb5175 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,30 @@ +name: Build + +on: + pull_request: + branches: + - main + - starter + - state + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set Up JDK + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + cache: 'gradle' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Build project and run local and device tests + run: ./gradlew :app:assembleDebug diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a2358d --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# built application files +*.apk +*.ap_ + +# Mac files +.DS_Store + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ + +# Ignore gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ +proguard-project.txt + +# Eclipse files +.project +.classpath +.settings/ + +# Android Studio/IDEA +*.iml +.idea \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3e0558e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# How to become a contributor and submit your own code + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement +(CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA] + (https://developers.google.com/open-source/cla/individual). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA] + (https://developers.google.com/open-source/cla/corporate). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the sample to which + you are contributing. Refer to the + [Google Cloud Platform Samples Style Guide] + (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the + recommended coding standards for this organization. +1. Ensure that your code has an appropriate set of unit tests which all pass. +1. Submit a pull request. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..08d4aa4 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +Kalkulator BMI +=============== + +Silahkan kembangkan aplikasi ini untuk melakukan perhitungan BMI + +Petunjuk lebih detil dapat dibaca di +https://docs.google.com/document/d/1iGiC0Bg3Bdcd2Maq45TYkCDUkZ5Ql51E/edit?rtpof=true + +Starter dimodifikasi dan terinspirasi dari: +https://developer.android.com/codelabs/basic-android-compose-calculate-tip#0 \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..f786714 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,85 @@ +/* + * 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. + */ + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") +} + +android { + compileSdk = 35 + + defaultConfig { + applicationId = "com.example.tiptime" + minSdk = 24 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + buildFeatures { + compose = true + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + namespace = "com.example.tiptime" +} + +dependencies { + + implementation(platform("androidx.compose:compose-bom:2024.12.01")) + implementation("androidx.activity:activity-compose:1.9.3") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-tooling") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + + testImplementation("junit:junit:4.13.2") + + androidTestImplementation(platform("androidx.compose:compose-bom:2024.12.01")) + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") + androidTestImplementation("androidx.test.ext:junit:1.2.1") + + debugImplementation("androidx.compose.ui:ui-test-manifest") +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e897dec --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/example/tiptime/MainActivity.kt b/app/src/main/java/com/example/tiptime/MainActivity.kt new file mode 100644 index 0000000..f0dc54c --- /dev/null +++ b/app/src/main/java/com/example/tiptime/MainActivity.kt @@ -0,0 +1,445 @@ +/* + * 🎃 Halloween BMI Calculator App + * Developed with ❤️ by Faris Naufal + * + * Licensed under the Apache License, Version 2.0 + * https://www.apache.org/licenses/LICENSE-2.0 + * + * 👻 Spooky BMI calculator with Halloween theme! + */ + +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.compose.animation.Crossfade +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.* +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +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 kotlin.math.pow + +// ---------------- HALLOWEEN COLORS ---------------- +val HalloweenOrange = Color(0xFFFF6B35) +val HalloweenPurple = Color(0xFF6A0572) +val HalloweenBlack = Color(0xFF1A1A1A) +val HalloweenGreen = Color(0xFF76BA1B) +val HalloweenYellow = Color(0xFFFFA500) +val SpookyGray = Color(0xFF2D2D2D) + +// ---------------- MAIN ACTIVITY ---------------- +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + setContent { + TipTimeTheme { + Surface(modifier = Modifier.fillMaxSize()) { + BMIApp() + } + } + } + } +} + +// ---------------- APP NAVIGATION ---------------- +@Composable +fun BMIApp() { + var currentScreen by remember { mutableStateOf("home") } + + Crossfade(targetState = currentScreen) { screen -> + when (screen) { + "home" -> HomeScreen(onNavigateToCalculator = { currentScreen = "calculator" }) + "calculator" -> BMICalculatorScreen(onNavigateBack = { currentScreen = "home" }) + } + } +} + +// ---------------- HOME SCREEN ---------------- +@Composable +fun HomeScreen(onNavigateToCalculator: () -> Unit) { + Box( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + colors = listOf(HalloweenPurple, HalloweenBlack, HalloweenOrange) + ) + ) + ) { + // Spooky decorative circles (like floating ghosts) + Canvas(modifier = Modifier.fillMaxSize()) { + drawCircle( + color = HalloweenOrange.copy(alpha = 0.1f), + radius = 180f, + center = Offset(x = size.width * 0.15f, y = size.height * 0.2f) + ) + drawCircle( + color = HalloweenGreen.copy(alpha = 0.08f), + radius = 220f, + center = Offset(x = size.width * 0.85f, y = size.height * 0.7f) + ) + drawCircle( + color = HalloweenYellow.copy(alpha = 0.06f), + radius = 120f, + center = Offset(x = size.width * 0.7f, y = size.height * 0.15f) + ) + drawCircle( + color = Color.White.copy(alpha = 0.05f), + radius = 90f, + center = Offset(x = size.width * 0.3f, y = size.height * 0.8f) + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .padding(32.dp) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "🎃 BMI Calculator", + style = MaterialTheme.typography.displayMedium.copy(fontWeight = FontWeight.Bold), + textAlign = TextAlign.Center, + color = HalloweenOrange + ) + + Text( + text = "👻 Halloween Edition 👻", + style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), + color = HalloweenYellow, + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 8.dp) + ) + + Text( + text = "by Faris Naufal", + style = MaterialTheme.typography.bodySmall, + color = Color.White.copy(alpha = 0.7f), + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 4.dp) + ) + + Spacer(Modifier.height(12.dp)) + + Text( + text = "Hitung Body Mass Index dengan suasana Halloween yang seram! 🦇", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + color = Color.White.copy(alpha = 0.9f) + ) + + Spacer(Modifier.height(48.dp)) + + Button( + onClick = onNavigateToCalculator, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors( + containerColor = HalloweenOrange, + contentColor = Color.White + ) + ) { + Text("🕷️ Mulai Hitung BMI", modifier = Modifier.padding(8.dp)) + } + } + } +} + +// ---------------- BMI CALCULATOR SCREEN ---------------- +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BMICalculatorScreen(onNavigateBack: () -> Unit) { + var heightInput by remember { mutableStateOf("") } + var weightInput by remember { mutableStateOf("") } + var useUSUnits by remember { mutableStateOf(false) } + + val height = heightInput.toDoubleOrNull() ?: 0.0 + val weight = weightInput.toDoubleOrNull() ?: 0.0 + val bmi = calculateBMI(height, weight, useUSUnits) + val category = calculateBMICategory(height, weight, useUSUnits) + + Scaffold( + topBar = { + TopAppBar( + title = { Text("🎃 Kalkulator BMI") }, + navigationIcon = { + IconButton(onClick = onNavigateBack) { + Icon(Icons.Default.ArrowBack, contentDescription = "Kembali") + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = HalloweenPurple, + titleContentColor = HalloweenOrange, + navigationIconContentColor = Color.White + ) + ) + } + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + colors = listOf(SpookyGray, HalloweenBlack) + ) + ) + .padding(innerPadding) + .padding(horizontal = 32.dp) + .verticalScroll(rememberScrollState()) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + UnitSwitchRow( + useUSUnits = useUSUnits, + onUnitChanged = { useUSUnits = it }, + modifier = Modifier.padding(bottom = 32.dp, top = 16.dp) + ) + + EditNumberField( + label = if (useUSUnits) "Tinggi (in) 👻" else "Tinggi (cm) 👻", + leadingIcon = R.drawable.number, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + value = heightInput, + onValueChanged = { heightInput = it }, + modifier = Modifier.fillMaxWidth().padding(bottom = 24.dp) + ) + + EditNumberField( + label = if (useUSUnits) "Berat (lbs) 🎃" else "Berat (kg) 🎃", + leadingIcon = R.drawable.number, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done + ), + value = weightInput, + onValueChanged = { weightInput = it }, + modifier = Modifier.fillMaxWidth().padding(bottom = 32.dp) + ) + + if (height > 0 && weight > 0) { + Card( + colors = CardDefaults.cardColors( + containerColor = SpookyGray + ), + shape = RoundedCornerShape(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "👻 BMI Anda 👻", + style = MaterialTheme.typography.titleLarge, + color = HalloweenYellow + ) + Text( + text = bmi, + style = MaterialTheme.typography.displayMedium.copy( + color = getBMICategoryColor(category), + fontWeight = FontWeight.Bold + ) + ) + Text( + text = category, + style = MaterialTheme.typography.bodyLarge.copy( + color = getBMICategoryColor(category), + fontWeight = FontWeight.SemiBold + ) + ) + } + } + + Spacer(modifier = Modifier.height(24.dp)) + + BMICategoryTable() + } + } + } +} + +// ---------------- BMI CATEGORY TABLE ---------------- +@Composable +fun BMICategoryTable() { + Card( + colors = CardDefaults.cardColors(containerColor = SpookyGray), + shape = RoundedCornerShape(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "🕸️ Tabel Kategori BMI 🕸️", + style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), + color = HalloweenOrange, + modifier = Modifier.padding(bottom = 8.dp) + ) + Divider(color = HalloweenPurple.copy(alpha = 0.5f), thickness = 2.dp) + Spacer(modifier = Modifier.height(8.dp)) + + BMICategoryRow("🧟 Underweight (Kurus)", "< 18.5", Color(0xFF64B5F6)) + BMICategoryRow("✨ Normal", "18.5 – 24.9", HalloweenGreen) + BMICategoryRow("🎃 Overweight (Gemuk)", "25 – 29.9", HalloweenYellow) + BMICategoryRow("👹 Obese (Obesitas)", "≥ 30", Color(0xFFE53935)) + } + } +} + +@Composable +fun BMICategoryRow(label: String, range: String, color: Color) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .background(color.copy(alpha = 0.2f), RoundedCornerShape(8.dp)) + .padding(12.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = label, + color = color, + fontSize = 15.sp, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = range, + color = color, + fontSize = 15.sp, + fontWeight = FontWeight.Medium + ) + } +} + +// ---------------- REUSABLE COMPONENTS ---------------- +@Composable +fun EditNumberField( + label: String, + @DrawableRes leadingIcon: Int, + keyboardOptions: KeyboardOptions, + value: String, + onValueChanged: (String) -> Unit, + modifier: Modifier = Modifier +) { + TextField( + value = value, + onValueChange = onValueChanged, + singleLine = true, + leadingIcon = { Icon(painterResource(id = leadingIcon), null, tint = HalloweenOrange) }, + label = { Text(label, color = HalloweenYellow) }, + placeholder = { Text("Masukkan nilai", color = Color.Gray) }, + keyboardOptions = keyboardOptions, + colors = TextFieldDefaults.colors( + focusedContainerColor = SpookyGray, + unfocusedContainerColor = SpookyGray, + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + focusedIndicatorColor = HalloweenOrange, + unfocusedIndicatorColor = HalloweenPurple + ), + modifier = modifier + ) +} + +@Composable +fun UnitSwitchRow( + useUSUnits: Boolean, + onUnitChanged: (Boolean) -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = if (useUSUnits) "🦇 Satuan: US (in, lbs)" else "🦇 Satuan: Metrik (cm, kg)", + color = Color.White + ) + Switch( + modifier = Modifier.fillMaxWidth().wrapContentWidth(Alignment.End), + checked = useUSUnits, + onCheckedChange = onUnitChanged, + colors = SwitchDefaults.colors( + checkedThumbColor = HalloweenOrange, + checkedTrackColor = HalloweenYellow, + uncheckedThumbColor = Color.Gray, + uncheckedTrackColor = SpookyGray + ) + ) + } +} + +// ---------------- LOGIC & UTILITIES ---------------- +@Composable +fun getBMICategoryColor(category: String): Color = when { + category.contains("Underweight", true) || category.contains("Kurus", true) -> Color(0xFF64B5F6) + category.contains("Normal", true) -> HalloweenGreen + category.contains("Overweight", true) || category.contains("Gemuk", true) -> HalloweenYellow + category.contains("Obese", true) || category.contains("Obesitas", true) -> Color(0xFFE53935) + else -> Color.White +} + +private fun calculateBMI(height: Double, weight: Double, useUSUnits: Boolean): String { + if (height <= 0 || weight <= 0) return "-" + val bmi = if (useUSUnits) (weight / height.pow(2)) * 703 else weight / (height / 100).pow(2) + return String.format("%.1f", bmi) +} + +private fun calculateBMICategory(height: Double, weight: Double, useUSUnits: Boolean): String { + if (height <= 0 || weight <= 0) return "-" + val bmi = if (useUSUnits) (weight / height.pow(2)) * 703 else weight / (height / 100).pow(2) + return when { + bmi < 18.5 -> "Underweight (Kurus)" + bmi < 25 -> "Normal" + bmi < 30 -> "Overweight (Gemuk)" + else -> "Obese (Obesitas)" + } +} + +// ---------------- PREVIEWS ---------------- +@Preview(showBackground = true) +@Composable +fun HomeScreenPreview() { + TipTimeTheme { HomeScreen(onNavigateToCalculator = {}) } +} + +@Preview(showBackground = true) +@Composable +fun BMICalculatorScreenPreview() { + TipTimeTheme { BMICalculatorScreen(onNavigateBack = {}) } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tiptime/ui/theme/Color.kt b/app/src/main/java/com/example/tiptime/ui/theme/Color.kt new file mode 100644 index 0000000..bc21042 --- /dev/null +++ b/app/src/main/java/com/example/tiptime/ui/theme/Color.kt @@ -0,0 +1,78 @@ +/* + * 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.ui.theme + +import androidx.compose.ui.graphics.Color + +val md_theme_light_primary = Color(0xFF984061) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFFFFD9E2) +val md_theme_light_onPrimaryContainer = Color(0xFF3E001D) +val md_theme_light_secondary = Color(0xFF754B9C) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFF1DBFF) +val md_theme_light_onSecondaryContainer = Color(0xFF2D0050) +val md_theme_light_tertiary = Color(0xFF984060) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFFFD9E2) +val md_theme_light_onTertiaryContainer = Color(0xFF3E001D) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xFFFAFCFF) +val md_theme_light_onBackground = Color(0xFF001F2A) +val md_theme_light_surface = Color(0xFFFAFCFF) +val md_theme_light_onSurface = Color(0xFF001F2A) +val md_theme_light_surfaceVariant = Color(0xFFF2DDE2) +val md_theme_light_onSurfaceVariant = Color(0xFF514347) +val md_theme_light_outline = Color(0xFF837377) +val md_theme_light_inverseOnSurface = Color(0xFFE1F4FF) +val md_theme_light_inverseSurface = Color(0xFF003547) +val md_theme_light_inversePrimary = Color(0xFFFFB0C8) +val md_theme_light_surfaceTint = Color(0xFF984061) +val md_theme_light_outlineVariant = Color(0xFFD5C2C6) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFFFFB0C8) +val md_theme_dark_onPrimary = Color(0xFF5E1133) +val md_theme_dark_primaryContainer = Color(0xFF7B2949) +val md_theme_dark_onPrimaryContainer = Color(0xFFFFD9E2) +val md_theme_dark_secondary = Color(0xFFDEB7FF) +val md_theme_dark_onSecondary = Color(0xFF44196A) +val md_theme_dark_secondaryContainer = Color(0xFF5C3382) +val md_theme_dark_onSecondaryContainer = Color(0xFFF1DBFF) +val md_theme_dark_tertiary = Color(0xFFFFB1C7) +val md_theme_dark_onTertiary = Color(0xFF5E1132) +val md_theme_dark_tertiaryContainer = Color(0xFF7B2948) +val md_theme_dark_onTertiaryContainer = Color(0xFFFFD9E2) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xFF001F2A) +val md_theme_dark_onBackground = Color(0xFFBFE9FF) +val md_theme_dark_surface = Color(0xFF001F2A) +val md_theme_dark_onSurface = Color(0xFFBFE9FF) +val md_theme_dark_surfaceVariant = Color(0xFF514347) +val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6) +val md_theme_dark_outline = Color(0xFF9E8C90) +val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) +val md_theme_dark_inverseSurface = Color(0xFFBFE9FF) +val md_theme_dark_inversePrimary = Color(0xFF984061) +val md_theme_dark_surfaceTint = Color(0xFFFFB0C8) +val md_theme_dark_outlineVariant = Color(0xFF514347) +val md_theme_dark_scrim = Color(0xFF000000) diff --git a/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt b/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt new file mode 100644 index 0000000..b8c9adb --- /dev/null +++ b/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt @@ -0,0 +1,115 @@ +/* + * 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.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val LightColorScheme = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, +) + +private val DarkColorScheme = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +@Composable +fun TipTimeTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + // Dynamic color in this app is turned off for learning purposes + dynamicColor: Boolean = false, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} diff --git a/app/src/main/java/com/example/tiptime/ui/theme/Type.kt b/app/src/main/java/com/example/tiptime/ui/theme/Type.kt new file mode 100644 index 0000000..a4cf6e6 --- /dev/null +++ b/app/src/main/java/com/example/tiptime/ui/theme/Type.kt @@ -0,0 +1,33 @@ +/* + * 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.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + displaySmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + fontSize = 36.sp, + lineHeight = 44.sp, + letterSpacing = 0.sp, + ) +) diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..a1fcc85 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..4bda16c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/number.xml b/app/src/main/res/drawable/number.xml new file mode 100644 index 0000000..9eaa138 --- /dev/null +++ b/app/src/main/res/drawable/number.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..264eaf4 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,22 @@ + + + + + + + + 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 new file mode 100644 index 0000000..264eaf4 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null 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 new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null 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 new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null 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 new file mode 100644 index 0000000..62b611d Binary files /dev/null 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 new file mode 100644 index 0000000..948a307 Binary files /dev/null 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 new file mode 100644 index 0000000..1b9a695 Binary files /dev/null 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 new file mode 100644 index 0000000..28d4b77 Binary files /dev/null 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 new file mode 100644 index 0000000..9287f50 Binary files /dev/null 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 new file mode 100644 index 0000000..aa7d642 Binary files /dev/null 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 new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..04e6db2 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,25 @@ + + + + BMI Calculator + Calculate BMI + Tinggi Badan + Berat Badan + Gunakan Unit USC (lbs/in)? + BMI Anda: %s + Kategori: %s + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..bbe5ce2 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,19 @@ + + + +