├── .github ├── ISSUE_TEMPLATE │ ├── calculate-a-custom-tip-issue-template.md │ └── intro-to-state-issue-template.md ├── renovate.json └── workflows │ └── main.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── tiptime │ │ ├── MainActivity.kt │ │ └── ui │ │ └── theme │ │ ├── Color.kt │ │ ├── Theme.kt │ │ └── Type.kt │ └── res │ ├── drawable │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ ├── money.xml │ └── percent.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── values │ ├── strings.xml │ └── themes.xml ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/ISSUE_TEMPLATE/calculate-a-custom-tip-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Calculate a custom tip issue template 3 | about: 'Issue template for Calculate a custom tip ' 4 | title: 'Calculate a custom tip: Android Basics in Compose ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **URL of codelab** 11 | 12 | 13 | **In which task and step of the codelab can this issue be found?** 14 | 15 | 16 | **Describe the problem** 17 | 18 | 19 | 20 | 21 | **Steps to reproduce?** 22 | 1. Go to... 23 | 2. Click on... 24 | 3. See error... 25 | 26 | **Versions** 27 | _Android Studio version:_ 28 | _API version of the emulator:_ 29 | 30 | 31 | **Additional information** 32 | _Include screenshots if they would be useful in clarifying the problem._ 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/intro-to-state-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Intro to state issue template 3 | about: 'Issue template for Intro to state in Compose ' 4 | title: 'Intro to state in Compose: Android Basics in Compose' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **URL of codelab** 11 | 12 | 13 | **In which task and step of the codelab can this issue be found?** 14 | 15 | 16 | **Describe the problem** 17 | 18 | 19 | 20 | 21 | **Steps to reproduce?** 22 | 1. Go to... 23 | 2. Click on... 24 | 3. See error... 25 | 26 | **Versions** 27 | _Android Studio version:_ 28 | _API version of the emulator:_ 29 | 30 | 31 | **Additional information** 32 | _Include screenshots if they would be useful in clarifying the problem._ 33 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>android/.github:renovate-config" 5 | ], 6 | "baseBranches": [ 7 | "main", 8 | "starter", 9 | "state" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - starter 8 | - state 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Set Up JDK 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: 'zulu' # See 'Supported distributions' for available options 20 | java-version: '17' 21 | cache: 'gradle' 22 | 23 | - name: Setup Gradle 24 | uses: gradle/actions/setup-gradle@v4 25 | 26 | - name: Setup Android SDK 27 | uses: android-actions/setup-android@v3 28 | 29 | - name: Build project and run local and device tests 30 | run: ./gradlew :app:assembleDebug 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Mac files 6 | .DS_Store 7 | 8 | # files for the dex VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # generated files 15 | bin/ 16 | gen/ 17 | 18 | # Ignore gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | proguard-project.txt 28 | 29 | # Eclipse files 30 | .project 31 | .classpath 32 | .settings/ 33 | 34 | # Android Studio/IDEA 35 | *.iml 36 | .idea -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a 27 | Contributor License Agreement (see details above). 28 | 1. Fork the desired repo, develop and test your code changes. 29 | 1. Ensure that your code adheres to the existing style in the sample to which 30 | you are contributing. Refer to the 31 | [Google Cloud Platform Samples Style Guide] 32 | (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 33 | recommended coding standards for this organization. 34 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 35 | 1. Submit a pull request. 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tip Time - Solution Code 2 | ================================= 3 | 4 | Solution code for the [Android Basics with Compose](https://developer.android.com/courses/android-basics-compose/course): Tip Time app. 5 | 6 | 7 | Introduction 8 | ------------ 9 | The Tip Time app contains various UI elements for calculating a tip, 10 | teaching about user input, and State in Compose. 11 | 12 | 13 | Pre-requisites 14 | -------------- 15 | * Experience with Kotlin syntax. 16 | * How to create and run a project in Android Studio. 17 | 18 | 19 | Getting Started 20 | --------------- 21 | 1. Install Android Studio, if you don't already have it. 22 | 2. Download the sample. 23 | 3. Import the sample into Android Studio. 24 | 4. Build and run the sample. 25 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | plugins { 18 | id("com.android.application") 19 | id("org.jetbrains.kotlin.android") 20 | id("org.jetbrains.kotlin.plugin.compose") 21 | } 22 | 23 | android { 24 | compileSdk = 35 25 | 26 | defaultConfig { 27 | applicationId = "com.example.tiptime" 28 | minSdk = 24 29 | targetSdk = 35 30 | versionCode = 1 31 | versionName = "1.0" 32 | 33 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 34 | vectorDrawables { 35 | useSupportLibrary = true 36 | } 37 | } 38 | 39 | buildTypes { 40 | release { 41 | isMinifyEnabled = false 42 | proguardFiles( 43 | getDefaultProguardFile("proguard-android-optimize.txt"), 44 | "proguard-rules.pro" 45 | ) 46 | } 47 | } 48 | compileOptions { 49 | sourceCompatibility = JavaVersion.VERSION_17 50 | targetCompatibility = JavaVersion.VERSION_17 51 | } 52 | kotlinOptions { 53 | jvmTarget = JavaVersion.VERSION_17.toString() 54 | } 55 | buildFeatures { 56 | compose = true 57 | } 58 | packaging { 59 | resources { 60 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 61 | } 62 | } 63 | namespace = "com.example.tiptime" 64 | } 65 | 66 | dependencies { 67 | 68 | implementation(platform("androidx.compose:compose-bom:2024.12.01")) 69 | implementation("androidx.activity:activity-compose:1.9.3") 70 | implementation("androidx.compose.material3:material3") 71 | implementation("androidx.compose.ui:ui") 72 | implementation("androidx.compose.ui:ui-tooling") 73 | implementation("androidx.compose.ui:ui-tooling-preview") 74 | implementation("androidx.core:core-ktx:1.15.0") 75 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") 76 | 77 | testImplementation("junit:junit:4.13.2") 78 | 79 | androidTestImplementation(platform("androidx.compose:compose-bom:2024.12.01")) 80 | androidTestImplementation("androidx.compose.ui:ui-test-junit4") 81 | androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") 82 | androidTestImplementation("androidx.test.ext:junit:1.2.1") 83 | 84 | debugImplementation("androidx.compose.ui:ui-test-manifest") 85 | } 86 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/tiptime/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.tiptime 17 | 18 | import android.os.Bundle 19 | import androidx.activity.ComponentActivity 20 | import androidx.activity.compose.setContent 21 | import androidx.activity.enableEdgeToEdge 22 | import androidx.annotation.DrawableRes 23 | import androidx.annotation.StringRes 24 | import androidx.compose.foundation.layout.Arrangement 25 | import androidx.compose.foundation.layout.Column 26 | import androidx.compose.foundation.layout.Row 27 | import androidx.compose.foundation.layout.Spacer 28 | import androidx.compose.foundation.layout.fillMaxSize 29 | import androidx.compose.foundation.layout.fillMaxWidth 30 | import androidx.compose.foundation.layout.height 31 | import androidx.compose.foundation.layout.padding 32 | import androidx.compose.foundation.layout.safeDrawingPadding 33 | import androidx.compose.foundation.layout.statusBarsPadding 34 | import androidx.compose.foundation.layout.wrapContentWidth 35 | import androidx.compose.foundation.rememberScrollState 36 | import androidx.compose.foundation.text.KeyboardOptions 37 | import androidx.compose.foundation.verticalScroll 38 | import androidx.compose.material3.Icon 39 | import androidx.compose.material3.MaterialTheme 40 | import androidx.compose.material3.Surface 41 | import androidx.compose.material3.Switch 42 | import androidx.compose.material3.Text 43 | import androidx.compose.material3.TextField 44 | import androidx.compose.runtime.Composable 45 | import androidx.compose.runtime.getValue 46 | import androidx.compose.runtime.mutableStateOf 47 | import androidx.compose.runtime.remember 48 | import androidx.compose.runtime.setValue 49 | import androidx.compose.ui.Alignment 50 | import androidx.compose.ui.Modifier 51 | import androidx.compose.ui.res.painterResource 52 | import androidx.compose.ui.res.stringResource 53 | import androidx.compose.ui.text.input.ImeAction 54 | import androidx.compose.ui.text.input.KeyboardType 55 | import androidx.compose.ui.tooling.preview.Preview 56 | import androidx.compose.ui.unit.dp 57 | import com.example.tiptime.ui.theme.TipTimeTheme 58 | import java.text.NumberFormat 59 | 60 | class MainActivity : ComponentActivity() { 61 | override fun onCreate(savedInstanceState: Bundle?) { 62 | enableEdgeToEdge() 63 | super.onCreate(savedInstanceState) 64 | setContent { 65 | TipTimeTheme { 66 | Surface( 67 | modifier = Modifier.fillMaxSize(), 68 | ) { 69 | TipTimeLayout() 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | @Composable 77 | fun TipTimeLayout() { 78 | var amountInput by remember { mutableStateOf("") } 79 | var tipInput by remember { mutableStateOf("") } 80 | var roundUp by remember { mutableStateOf(false) } 81 | 82 | val amount = amountInput.toDoubleOrNull() ?: 0.0 83 | val tipPercent = tipInput.toDoubleOrNull() ?: 0.0 84 | val tip = calculateTip(amount, tipPercent, roundUp) 85 | 86 | Column( 87 | modifier = Modifier 88 | .statusBarsPadding() 89 | .padding(horizontal = 40.dp) 90 | .verticalScroll(rememberScrollState()) 91 | .safeDrawingPadding(), 92 | horizontalAlignment = Alignment.CenterHorizontally, 93 | verticalArrangement = Arrangement.Center 94 | ) { 95 | Text( 96 | text = stringResource(R.string.calculate_tip), 97 | modifier = Modifier 98 | .padding(bottom = 16.dp, top = 40.dp) 99 | .align(alignment = Alignment.Start) 100 | ) 101 | EditNumberField( 102 | label = R.string.bill_amount, 103 | leadingIcon = R.drawable.money, 104 | keyboardOptions = KeyboardOptions.Default.copy( 105 | keyboardType = KeyboardType.Number, 106 | imeAction = ImeAction.Next 107 | ), 108 | value = amountInput, 109 | onValueChanged = { amountInput = it }, 110 | modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), 111 | ) 112 | EditNumberField( 113 | label = R.string.how_was_the_service, 114 | leadingIcon = R.drawable.percent, 115 | keyboardOptions = KeyboardOptions.Default.copy( 116 | keyboardType = KeyboardType.Number, 117 | imeAction = ImeAction.Done 118 | ), 119 | value = tipInput, 120 | onValueChanged = { tipInput = it }, 121 | modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), 122 | ) 123 | RoundTheTipRow( 124 | roundUp = roundUp, 125 | onRoundUpChanged = { roundUp = it }, 126 | modifier = Modifier.padding(bottom = 32.dp) 127 | ) 128 | Text( 129 | text = stringResource(R.string.tip_amount, tip), 130 | style = MaterialTheme.typography.displaySmall 131 | ) 132 | Spacer(modifier = Modifier.height(150.dp)) 133 | } 134 | } 135 | 136 | @Composable 137 | fun EditNumberField( 138 | @StringRes label: Int, 139 | @DrawableRes leadingIcon: Int, 140 | keyboardOptions: KeyboardOptions, 141 | value: String, 142 | onValueChanged: (String) -> Unit, 143 | modifier: Modifier = Modifier 144 | ) { 145 | TextField( 146 | value = value, 147 | singleLine = true, 148 | leadingIcon = { Icon(painter = painterResource(id = leadingIcon), null) }, 149 | modifier = modifier, 150 | onValueChange = onValueChanged, 151 | label = { Text(stringResource(label)) }, 152 | keyboardOptions = keyboardOptions 153 | ) 154 | } 155 | 156 | @Composable 157 | fun RoundTheTipRow( 158 | roundUp: Boolean, 159 | onRoundUpChanged: (Boolean) -> Unit, 160 | modifier: Modifier = Modifier 161 | ) { 162 | Row( 163 | modifier = modifier.fillMaxWidth(), 164 | verticalAlignment = Alignment.CenterVertically 165 | ) { 166 | Text(text = stringResource(R.string.round_up_tip)) 167 | Switch( 168 | modifier = Modifier 169 | .fillMaxWidth() 170 | .wrapContentWidth(Alignment.End), 171 | checked = roundUp, 172 | onCheckedChange = onRoundUpChanged 173 | ) 174 | } 175 | } 176 | 177 | /** 178 | * Calculates the tip based on the user input and format the tip amount 179 | * according to the local currency. 180 | * Example would be "$10.00". 181 | */ 182 | private fun calculateTip(amount: Double, tipPercent: Double = 15.0, roundUp: Boolean): String { 183 | var tip = tipPercent / 100 * amount 184 | if (roundUp) { 185 | tip = kotlin.math.ceil(tip) 186 | } 187 | return NumberFormat.getCurrencyInstance().format(tip) 188 | } 189 | 190 | @Preview(showBackground = true) 191 | @Composable 192 | fun TipTimeLayoutPreview() { 193 | TipTimeTheme { 194 | TipTimeLayout() 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/tiptime/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.tiptime.ui.theme 17 | 18 | import androidx.compose.ui.graphics.Color 19 | 20 | val md_theme_light_primary = Color(0xFF984061) 21 | val md_theme_light_onPrimary = Color(0xFFFFFFFF) 22 | val md_theme_light_primaryContainer = Color(0xFFFFD9E2) 23 | val md_theme_light_onPrimaryContainer = Color(0xFF3E001D) 24 | val md_theme_light_secondary = Color(0xFF754B9C) 25 | val md_theme_light_onSecondary = Color(0xFFFFFFFF) 26 | val md_theme_light_secondaryContainer = Color(0xFFF1DBFF) 27 | val md_theme_light_onSecondaryContainer = Color(0xFF2D0050) 28 | val md_theme_light_tertiary = Color(0xFF984060) 29 | val md_theme_light_onTertiary = Color(0xFFFFFFFF) 30 | val md_theme_light_tertiaryContainer = Color(0xFFFFD9E2) 31 | val md_theme_light_onTertiaryContainer = Color(0xFF3E001D) 32 | val md_theme_light_error = Color(0xFFBA1A1A) 33 | val md_theme_light_errorContainer = Color(0xFFFFDAD6) 34 | val md_theme_light_onError = Color(0xFFFFFFFF) 35 | val md_theme_light_onErrorContainer = Color(0xFF410002) 36 | val md_theme_light_background = Color(0xFFFAFCFF) 37 | val md_theme_light_onBackground = Color(0xFF001F2A) 38 | val md_theme_light_surface = Color(0xFFFAFCFF) 39 | val md_theme_light_onSurface = Color(0xFF001F2A) 40 | val md_theme_light_surfaceVariant = Color(0xFFF2DDE2) 41 | val md_theme_light_onSurfaceVariant = Color(0xFF514347) 42 | val md_theme_light_outline = Color(0xFF837377) 43 | val md_theme_light_inverseOnSurface = Color(0xFFE1F4FF) 44 | val md_theme_light_inverseSurface = Color(0xFF003547) 45 | val md_theme_light_inversePrimary = Color(0xFFFFB0C8) 46 | val md_theme_light_surfaceTint = Color(0xFF984061) 47 | val md_theme_light_outlineVariant = Color(0xFFD5C2C6) 48 | val md_theme_light_scrim = Color(0xFF000000) 49 | 50 | val md_theme_dark_primary = Color(0xFFFFB0C8) 51 | val md_theme_dark_onPrimary = Color(0xFF5E1133) 52 | val md_theme_dark_primaryContainer = Color(0xFF7B2949) 53 | val md_theme_dark_onPrimaryContainer = Color(0xFFFFD9E2) 54 | val md_theme_dark_secondary = Color(0xFFDEB7FF) 55 | val md_theme_dark_onSecondary = Color(0xFF44196A) 56 | val md_theme_dark_secondaryContainer = Color(0xFF5C3382) 57 | val md_theme_dark_onSecondaryContainer = Color(0xFFF1DBFF) 58 | val md_theme_dark_tertiary = Color(0xFFFFB1C7) 59 | val md_theme_dark_onTertiary = Color(0xFF5E1132) 60 | val md_theme_dark_tertiaryContainer = Color(0xFF7B2948) 61 | val md_theme_dark_onTertiaryContainer = Color(0xFFFFD9E2) 62 | val md_theme_dark_error = Color(0xFFFFB4AB) 63 | val md_theme_dark_errorContainer = Color(0xFF93000A) 64 | val md_theme_dark_onError = Color(0xFF690005) 65 | val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) 66 | val md_theme_dark_background = Color(0xFF001F2A) 67 | val md_theme_dark_onBackground = Color(0xFFBFE9FF) 68 | val md_theme_dark_surface = Color(0xFF001F2A) 69 | val md_theme_dark_onSurface = Color(0xFFBFE9FF) 70 | val md_theme_dark_surfaceVariant = Color(0xFF514347) 71 | val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6) 72 | val md_theme_dark_outline = Color(0xFF9E8C90) 73 | val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) 74 | val md_theme_dark_inverseSurface = Color(0xFFBFE9FF) 75 | val md_theme_dark_inversePrimary = Color(0xFF984061) 76 | val md_theme_dark_surfaceTint = Color(0xFFFFB0C8) 77 | val md_theme_dark_outlineVariant = Color(0xFF514347) 78 | val md_theme_dark_scrim = Color(0xFF000000) 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/tiptime/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.tiptime.ui.theme 17 | 18 | import android.os.Build 19 | import androidx.compose.foundation.isSystemInDarkTheme 20 | import androidx.compose.material3.MaterialTheme 21 | import androidx.compose.material3.darkColorScheme 22 | import androidx.compose.material3.dynamicDarkColorScheme 23 | import androidx.compose.material3.dynamicLightColorScheme 24 | import androidx.compose.material3.lightColorScheme 25 | import androidx.compose.runtime.Composable 26 | import androidx.compose.ui.platform.LocalContext 27 | 28 | private val LightColorScheme = lightColorScheme( 29 | primary = md_theme_light_primary, 30 | onPrimary = md_theme_light_onPrimary, 31 | primaryContainer = md_theme_light_primaryContainer, 32 | onPrimaryContainer = md_theme_light_onPrimaryContainer, 33 | secondary = md_theme_light_secondary, 34 | onSecondary = md_theme_light_onSecondary, 35 | secondaryContainer = md_theme_light_secondaryContainer, 36 | onSecondaryContainer = md_theme_light_onSecondaryContainer, 37 | tertiary = md_theme_light_tertiary, 38 | onTertiary = md_theme_light_onTertiary, 39 | tertiaryContainer = md_theme_light_tertiaryContainer, 40 | onTertiaryContainer = md_theme_light_onTertiaryContainer, 41 | error = md_theme_light_error, 42 | errorContainer = md_theme_light_errorContainer, 43 | onError = md_theme_light_onError, 44 | onErrorContainer = md_theme_light_onErrorContainer, 45 | background = md_theme_light_background, 46 | onBackground = md_theme_light_onBackground, 47 | surface = md_theme_light_surface, 48 | onSurface = md_theme_light_onSurface, 49 | surfaceVariant = md_theme_light_surfaceVariant, 50 | onSurfaceVariant = md_theme_light_onSurfaceVariant, 51 | outline = md_theme_light_outline, 52 | inverseOnSurface = md_theme_light_inverseOnSurface, 53 | inverseSurface = md_theme_light_inverseSurface, 54 | inversePrimary = md_theme_light_inversePrimary, 55 | surfaceTint = md_theme_light_surfaceTint, 56 | outlineVariant = md_theme_light_outlineVariant, 57 | scrim = md_theme_light_scrim, 58 | ) 59 | 60 | private val DarkColorScheme = darkColorScheme( 61 | primary = md_theme_dark_primary, 62 | onPrimary = md_theme_dark_onPrimary, 63 | primaryContainer = md_theme_dark_primaryContainer, 64 | onPrimaryContainer = md_theme_dark_onPrimaryContainer, 65 | secondary = md_theme_dark_secondary, 66 | onSecondary = md_theme_dark_onSecondary, 67 | secondaryContainer = md_theme_dark_secondaryContainer, 68 | onSecondaryContainer = md_theme_dark_onSecondaryContainer, 69 | tertiary = md_theme_dark_tertiary, 70 | onTertiary = md_theme_dark_onTertiary, 71 | tertiaryContainer = md_theme_dark_tertiaryContainer, 72 | onTertiaryContainer = md_theme_dark_onTertiaryContainer, 73 | error = md_theme_dark_error, 74 | errorContainer = md_theme_dark_errorContainer, 75 | onError = md_theme_dark_onError, 76 | onErrorContainer = md_theme_dark_onErrorContainer, 77 | background = md_theme_dark_background, 78 | onBackground = md_theme_dark_onBackground, 79 | surface = md_theme_dark_surface, 80 | onSurface = md_theme_dark_onSurface, 81 | surfaceVariant = md_theme_dark_surfaceVariant, 82 | onSurfaceVariant = md_theme_dark_onSurfaceVariant, 83 | outline = md_theme_dark_outline, 84 | inverseOnSurface = md_theme_dark_inverseOnSurface, 85 | inverseSurface = md_theme_dark_inverseSurface, 86 | inversePrimary = md_theme_dark_inversePrimary, 87 | surfaceTint = md_theme_dark_surfaceTint, 88 | outlineVariant = md_theme_dark_outlineVariant, 89 | scrim = md_theme_dark_scrim, 90 | ) 91 | 92 | @Composable 93 | fun TipTimeTheme( 94 | darkTheme: Boolean = isSystemInDarkTheme(), 95 | // Dynamic color is available on Android 12+ 96 | // Dynamic color in this app is turned off for learning purposes 97 | dynamicColor: Boolean = false, 98 | content: @Composable () -> Unit 99 | ) { 100 | val colorScheme = when { 101 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 102 | val context = LocalContext.current 103 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 104 | } 105 | 106 | darkTheme -> DarkColorScheme 107 | else -> LightColorScheme 108 | } 109 | 110 | MaterialTheme( 111 | colorScheme = colorScheme, 112 | typography = Typography, 113 | content = content 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/tiptime/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.tiptime.ui.theme 17 | 18 | import androidx.compose.material3.Typography 19 | import androidx.compose.ui.text.TextStyle 20 | import androidx.compose.ui.text.font.FontFamily 21 | import androidx.compose.ui.text.font.FontWeight 22 | import androidx.compose.ui.unit.sp 23 | 24 | // Set of Material typography styles to start with 25 | val Typography = Typography( 26 | displaySmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Bold, 29 | fontSize = 36.sp, 30 | lineHeight = 44.sp, 31 | letterSpacing = 0.sp, 32 | ) 33 | ) 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 175 | 180 | 185 | 186 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 24 | 25 | 31 | 34 | 37 | 38 | 39 | 40 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/money.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/percent.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator/b029b3dd10da273099d21d5e214d1350afb43c16/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | Tip Time 19 | Calculate Tip 20 | Bill Amount 21 | Tip Percentage 22 | Round up tip? 23 | Tip Amount: %s 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 |