├── .github └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .idea └── copyright │ ├── Touchlane.xml │ └── profiles_settings.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── touchlane │ │ └── gridpad │ │ └── example │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── touchlane │ │ │ └── gridpad │ │ │ └── example │ │ │ ├── GridPadExampleApplication.kt │ │ │ ├── MainActivity.kt │ │ │ └── ui │ │ │ ├── component │ │ │ ├── BlueprintBox.kt │ │ │ ├── EngineeringCalculatorPad.kt │ │ │ ├── InteractivePinPad.kt │ │ │ ├── PadButton.kt │ │ │ ├── PinPad.kt │ │ │ ├── SimpleCalculatorPad.kt │ │ │ └── SimplePriorityCalculatorPad.kt │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-anydpi-v33 │ │ └── ic_launcher.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 │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── touchlane │ └── gridpad │ └── example │ └── ExampleUnitTest.kt ├── build-logic ├── convention │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── AndroidApplicationConventionPlugin.kt │ │ ├── AndroidComposeConventionPlugin.kt │ │ ├── AndroidDetektConventionPlugin.kt │ │ ├── AndroidJacocoConventionPlugin.kt │ │ ├── AndroidKotlinConventionPlugin.kt │ │ ├── AndroidKotlinExplicitApiConventionPlugin.kt │ │ ├── AndroidLibraryConventionPlugin.kt │ │ ├── PublishNexusModuleConventionPlugin.kt │ │ ├── PublishNexusProjectConventionPlugin.kt │ │ └── com │ │ └── touchlane │ │ └── gridpad │ │ ├── AndroidApplication.kt │ │ ├── AndroidCommon.kt │ │ ├── AndroidCompose.kt │ │ ├── AndroidJacoco.kt │ │ ├── AndroidLibrary.kt │ │ ├── AndroidLibraryPublishing.kt │ │ ├── Kotlin.kt │ │ ├── PublishingSetupRepository.kt │ │ ├── PublishingUpload.kt │ │ └── publishing │ │ ├── EnvironmentPublishingCredentialsLoader.kt │ │ ├── FilePublishingCredentialsLoader.kt │ │ ├── PublishingCredentialsDelegate.kt │ │ ├── PublishingCredentialsLoader.kt │ │ └── PublishingProperties.kt ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── settings.gradle.kts ├── build.gradle.kts ├── config └── detekt │ └── detekt.yml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gridpad ├── .gitignore ├── api │ └── gridpad.api ├── artifact.properties ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── touchlane │ │ └── gridpad │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── touchlane │ │ └── gridpad │ │ ├── GridPadCells.kt │ │ ├── GridPadDiagnosticLogger.kt │ │ ├── GridPadDsl.kt │ │ ├── GridPadItemScope.kt │ │ ├── GridPadItemScopeImpl.kt │ │ ├── GridPadPlacementPolicy.kt │ │ ├── GridPadScopeImpl.kt │ │ ├── GridPadScopeMarker.kt │ │ └── GridPadSpanAnchor.kt │ └── test │ └── kotlin │ └── com │ └── touchlane │ └── gridpad │ ├── GridPadCellsUnitTest.kt │ ├── GridPadDiagnosticLoggerUnitTest.kt │ ├── GridPadPlacementPolicyUnitTest.kt │ ├── GridPadScopeTest.kt │ └── LoggerTest.kt └── settings.gradle.kts /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | # Run this workflow when a new GitHub release is created 6 | types: [released] 7 | 8 | jobs: 9 | publish: 10 | name: Release build and publish 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v2 15 | - name: Set up JDK 17 16 | uses: actions/setup-java@v2 17 | with: 18 | distribution: adopt 19 | java-version: 17 20 | 21 | # Builds the release artifacts of the library 22 | - name: Release build 23 | run: ./gradlew :gridpad:assemble 24 | 25 | # Runs upload, and then closes & releases the repository 26 | - name: Publish to MavenCentral 27 | run: ./gradlew publishGridpadPublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository 28 | env: 29 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 30 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 31 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} 32 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 33 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 34 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Quality Control 2 | 3 | on: 4 | push: 5 | branches: ['*'] 6 | 7 | pull_request: 8 | branches: ['*'] 9 | 10 | jobs: 11 | quality-control: 12 | name: Quality Control 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v2 17 | - name: Set up JDK 17 18 | uses: actions/setup-java@v2 19 | with: 20 | distribution: adopt 21 | java-version: 17 22 | 23 | # Run static analyze 24 | - name: Run static analyze 25 | run: ./gradlew :gridpad:detekt 26 | 27 | # Run unit testing 28 | - name: Run unit testing 29 | run: ./gradlew :gridpad:test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | /.idea/inspectionProfiles/ 87 | /.idea/.gitignore 88 | /.idea/.name 89 | /.idea/androidTestResultsUserPreferences.xml 90 | /.idea/compiler.xml 91 | /.idea/kotlinc.xml 92 | /.idea/misc.xml 93 | /.idea/deploymentTargetDropDown.xml 94 | -------------------------------------------------------------------------------- /.idea/copyright/Touchlane.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## Version 1.1.2 5 | 6 | _2023-07-13_ 7 | 8 | * **Fix**: size distribution algorithm 9 | * Dependency updates 10 | 11 | ## Version 1.1.1 12 | 13 | _2023-05-23_ 14 | 15 | * Dependency updates 16 | 17 | ## Version 1.1.0 18 | 19 | _2023-03-06_ 20 | 21 | * **API Change**: helper extension signature from 22 | `GridPadCellSize.Companion.weight(sizes: FloatArray)` to 23 | `GridPadCellSize.Companion.weight(vararg sizes: Float)` 24 | 25 | ## Version 1.0.0 26 | 27 | _2022-12-28_ 28 | 29 | * **New**: added `GridPadPlacementPolicy` class that used to control implicit placement policy 30 | for items 31 | * **New**: added `GridPadDiagnosticLogger` diagnostic class to receive information about skipped 32 | items 33 | * **API Change**: to `GridPad` added `placementPolicy` property 34 | * **API Change**: `GridPadScope.item()` split to two methods - explicit and implicit 35 | * **Behavior Change**: implicit placement of elements depends on `placementPolicy` property 36 | * **Behavior Change**: span expands depends on `placementPolicy` property 37 | 38 | The new API shouldn't affect your code by default. Only one case can be affected by library 39 | update - cases when only one of the `row` or `column` parameters was defined in GridPad.item. 40 | This way of using is no more acceptable for not enough clear behavior reasons. 41 | 42 | ## Version 0.0.4 43 | 44 | _2022-12-20_ 45 | 46 | * **API Change**: GridPad DSL is limited to a Kotlin context receiver 47 | * **Fix**: size distribution algorithm 48 | * test coverage improvement 49 | 50 | ## Version 0.0.3 51 | 52 | _2022-12-09_ 53 | 54 | * **Fix**: added missing source code 55 | 56 | ## Version 0.0.2 57 | 58 | _2022-12-07_ 59 | 60 | * **Remove**: unused dependencies 61 | 62 | ## Version 0.0.1 63 | 64 | _2022-12-07_ 65 | 66 | * First alpha release 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Touchlane LLC tech@touchlane.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | @file:Suppress("UnstableApiUsage") 26 | @Suppress("DSL_SCOPE_VIOLATION") // Remove when fixed https://youtrack.jetbrains.com/issue/KTIJ-19369 27 | plugins { 28 | id("gridpad.android.application") 29 | id("gridpad.jetbrains.kotlin.android") 30 | id("gridpad.android.compose") 31 | } 32 | 33 | android { 34 | namespace = "com.touchlane.gridpad.example" 35 | 36 | defaultConfig { 37 | applicationId = "com.touchlane.gridpad.example" 38 | versionCode = 1 39 | versionName = "1.0" 40 | 41 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | getByName("release") { 46 | isMinifyEnabled = false 47 | proguardFiles( 48 | getDefaultProguardFile("proguard-android-optimize.txt"), 49 | "proguard-rules.pro" 50 | ) 51 | } 52 | } 53 | } 54 | 55 | dependencies { 56 | implementation(project(":gridpad")) 57 | implementation(libs.androidx.activity.compose) 58 | implementation(libs.androidx.compose.material.icons) 59 | implementation(libs.androidx.compose.material3) 60 | implementation(libs.androidx.compose.runtime) 61 | implementation(libs.androidx.compose.ui) 62 | implementation(libs.androidx.compose.ui.tooling.preview) 63 | implementation(libs.androidx.core.ktx) 64 | implementation(libs.androidx.lifecycle.runtime.ktx) 65 | testImplementation(libs.junit4) 66 | androidTestImplementation(libs.androidx.test.ext) 67 | androidTestImplementation(libs.androidx.test.espresso.core) 68 | androidTestImplementation(libs.androidx.compose.ui.test) 69 | debugImplementation(libs.androidx.compose.ui.tooling) 70 | debugImplementation(libs.androidx.compose.ui.testManifest) 71 | } -------------------------------------------------------------------------------- /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.kts. 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/androidTest/java/com/touchlane/gridpad/example/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example 26 | 27 | import androidx.test.platform.app.InstrumentationRegistry 28 | import androidx.test.ext.junit.runners.AndroidJUnit4 29 | 30 | import org.junit.Test 31 | import org.junit.runner.RunWith 32 | 33 | import org.junit.Assert.* 34 | 35 | /** 36 | * Instrumented test, which will execute on an Android device. 37 | * 38 | * See [testing documentation](http://d.android.com/tools/testing). 39 | */ 40 | @RunWith(AndroidJUnit4::class) 41 | class ExampleInstrumentedTest { 42 | @Test 43 | fun useAppContext() { 44 | // Context of the app under test. 45 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 46 | assertEquals("com.touchlane.gridpad.example", appContext.packageName) 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 27 | 28 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/GridPadExampleApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example 26 | 27 | import android.app.Application 28 | import android.util.Log 29 | import com.touchlane.gridpad.GridPadDiagnosticLogger 30 | 31 | class GridPadExampleApplication : Application() { 32 | 33 | override fun onCreate() { 34 | super.onCreate() 35 | GridPadDiagnosticLogger.skippingItemListener = { message -> 36 | Log.w("GridPad", message) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/BlueprintBox.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.foundation.Canvas 28 | import androidx.compose.foundation.background 29 | import androidx.compose.foundation.layout.Box 30 | import androidx.compose.foundation.layout.fillMaxSize 31 | import androidx.compose.foundation.layout.padding 32 | import androidx.compose.foundation.layout.wrapContentSize 33 | import androidx.compose.material3.MaterialTheme 34 | import androidx.compose.material3.Text 35 | import androidx.compose.runtime.Composable 36 | import androidx.compose.ui.Modifier 37 | import androidx.compose.ui.graphics.Color 38 | import androidx.compose.ui.graphics.PathEffect 39 | import androidx.compose.ui.graphics.drawscope.Stroke 40 | import androidx.compose.ui.platform.LocalDensity 41 | import androidx.compose.ui.text.style.TextAlign 42 | import androidx.compose.ui.tooling.preview.Preview 43 | import androidx.compose.ui.unit.dp 44 | import com.touchlane.gridpad.example.ui.theme.AndreaBlue 45 | import com.touchlane.gridpad.example.ui.theme.HeatWave 46 | 47 | @Composable 48 | fun BlueprintBox() { 49 | val stroke = Stroke( 50 | width = with(LocalDensity.current) { 1.dp.toPx() }, 51 | pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) 52 | ) 53 | Box( 54 | modifier = Modifier 55 | .fillMaxSize() 56 | .padding(1.dp) 57 | ) { 58 | Canvas(modifier = Modifier.fillMaxSize()) { 59 | drawRect(color = Color.White, style = stroke) 60 | } 61 | } 62 | } 63 | 64 | @Composable 65 | fun ContentBlueprintBox(text: String = "") { 66 | val stroke = Stroke( 67 | width = with(LocalDensity.current) { 2.dp.toPx() }, 68 | pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) 69 | ) 70 | Box( 71 | modifier = Modifier 72 | .fillMaxSize() 73 | .padding(1.dp) 74 | .background(Color.White) 75 | ) { 76 | Canvas(modifier = Modifier.fillMaxSize()) { 77 | drawRect(color = HeatWave, style = stroke) 78 | } 79 | Text( 80 | modifier = Modifier 81 | .fillMaxSize() 82 | .wrapContentSize(), 83 | text = text, 84 | style = MaterialTheme.typography.bodyMedium, 85 | color = AndreaBlue, 86 | textAlign = TextAlign.Center 87 | ) 88 | } 89 | } 90 | 91 | @Preview(widthDp = 86, heightDp = 86) 92 | @Composable 93 | fun BlueprintBoxPreview() { 94 | BlueprintBox() 95 | } 96 | 97 | @Preview(widthDp = 86, heightDp = 86) 98 | @Composable 99 | fun ContentBlueprintBoxPreview() { 100 | ContentBlueprintBox("[0;0]") 101 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/InteractivePinPad.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.foundation.background 28 | import androidx.compose.foundation.layout.* 29 | import androidx.compose.foundation.shape.RoundedCornerShape 30 | import androidx.compose.material3.MaterialTheme 31 | import androidx.compose.material3.Text 32 | import androidx.compose.runtime.* 33 | import androidx.compose.ui.Alignment 34 | import androidx.compose.ui.Modifier 35 | import androidx.compose.ui.draw.clip 36 | import androidx.compose.ui.tooling.preview.Preview 37 | import androidx.compose.ui.unit.dp 38 | import com.touchlane.gridpad.example.ui.theme.GridPadExampleTheme 39 | 40 | @Composable 41 | fun InteractivePinPad(modifier: Modifier = Modifier) { 42 | var digits by remember { mutableStateOf("") } 43 | Column(modifier = modifier) { 44 | Box( 45 | modifier = Modifier 46 | .fillMaxWidth() 47 | .padding(8.dp) 48 | .clip(RoundedCornerShape(size = 8.dp)) 49 | .background(MaterialTheme.colorScheme.surface), 50 | contentAlignment = Alignment.Center 51 | ) { 52 | Text( 53 | modifier = Modifier.padding(8.dp), 54 | text = digits, 55 | style = MaterialTheme.typography.displayMedium, 56 | color = MaterialTheme.colorScheme.onSurface 57 | ) 58 | } 59 | Spacer(modifier = Modifier.height(16.dp)) 60 | PinPad( 61 | modifier = Modifier 62 | .fillMaxWidth() 63 | .aspectRatio(1.2f) 64 | ) { action -> 65 | when (action) { 66 | 'r' -> { 67 | if (digits.isNotEmpty()) { 68 | digits = digits.substring(0, digits.length - 1) 69 | } 70 | } 71 | else -> { 72 | if (digits.length < 6) { 73 | digits += action 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | @Preview(showBackground = true, widthDp = 300) 82 | @Composable 83 | fun InteractivePinPadPreview() { 84 | GridPadExampleTheme { 85 | InteractivePinPad() 86 | } 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/PadButton.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.animation.core.LinearOutSlowInEasing 28 | import androidx.compose.animation.core.animateDpAsState 29 | import androidx.compose.animation.core.tween 30 | import androidx.compose.foundation.interaction.MutableInteractionSource 31 | import androidx.compose.foundation.interaction.collectIsPressedAsState 32 | import androidx.compose.foundation.layout.* 33 | import androidx.compose.foundation.shape.RoundedCornerShape 34 | import androidx.compose.material.icons.Icons 35 | import androidx.compose.material.icons.filled.Backspace 36 | import androidx.compose.material3.* 37 | import androidx.compose.runtime.* 38 | import androidx.compose.ui.Modifier 39 | import androidx.compose.ui.graphics.Color 40 | import androidx.compose.ui.graphics.takeOrElse 41 | import androidx.compose.ui.graphics.vector.ImageVector 42 | import androidx.compose.ui.text.TextStyle 43 | import androidx.compose.ui.tooling.preview.Preview 44 | import androidx.compose.ui.unit.Dp 45 | import androidx.compose.ui.unit.dp 46 | import com.touchlane.gridpad.example.ui.theme.GridPadExampleTheme 47 | 48 | @Composable 49 | fun LargeTextPadButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) { 50 | TextPadButton( 51 | text = text, 52 | onClick = onClick, 53 | style = MaterialTheme.typography.displaySmall, 54 | modifier = modifier 55 | ) 56 | } 57 | 58 | @Composable 59 | fun MediumTextPadButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) { 60 | TextPadButton( 61 | text = text, 62 | onClick = onClick, 63 | style = MaterialTheme.typography.headlineMedium, 64 | modifier = modifier 65 | ) 66 | } 67 | 68 | @Composable 69 | fun SmallTextPadButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) { 70 | TextPadButton( 71 | text = text, 72 | onClick = onClick, 73 | style = MaterialTheme.typography.titleMedium, 74 | modifier = modifier 75 | ) 76 | } 77 | 78 | @Composable 79 | fun TextPadButton( 80 | text: String, 81 | style: TextStyle, 82 | onClick: () -> Unit, 83 | modifier: Modifier = Modifier 84 | ) { 85 | PadButton(onClick = onClick, modifier = modifier) { 86 | Text(text = text, style = style, color = PadButtonTheme.colors.content) 87 | } 88 | } 89 | 90 | @Composable 91 | fun IconPadButton(icon: ImageVector, onClick: () -> Unit, modifier: Modifier = Modifier) { 92 | PadButton(onClick = onClick, modifier = modifier) { 93 | val tint = PadButtonTheme.colors.content.takeOrElse { 94 | LocalContentColor.current 95 | } 96 | Icon( 97 | icon, 98 | contentDescription = null, 99 | modifier = Modifier.size(32.dp), 100 | tint = tint 101 | ) 102 | } 103 | } 104 | 105 | @Composable 106 | fun PadButton( 107 | onClick: () -> Unit, 108 | modifier: Modifier = Modifier, 109 | content: @Composable RowScope.() -> Unit 110 | ) { 111 | val interactionSource = remember { MutableInteractionSource() } 112 | val isPressed by interactionSource.collectIsPressedAsState() 113 | val corner: Dp by animateDpAsState( 114 | if (isPressed) 20.dp else 50.dp, 115 | tween(150, easing = LinearOutSlowInEasing) 116 | ) 117 | val containerColor = PadButtonTheme.colors.background.takeOrElse { 118 | MaterialTheme.colorScheme.primary 119 | } 120 | val contentColor = PadButtonTheme.colors.content.takeOrElse { 121 | MaterialTheme.colorScheme.onPrimary 122 | } 123 | val disabledContainerColor = containerColor.takeOrElse { 124 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f) 125 | } 126 | val disabledContentColor = contentColor.takeOrElse { 127 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) 128 | } 129 | val colors = ButtonDefaults.buttonColors( 130 | containerColor = containerColor, 131 | contentColor = contentColor, 132 | disabledContainerColor = disabledContainerColor, 133 | disabledContentColor = disabledContentColor 134 | ) 135 | Button( 136 | onClick = onClick, 137 | modifier = modifier 138 | .fillMaxSize() 139 | .padding(4.dp), 140 | shape = RoundedCornerShape(corner), 141 | colors = colors, 142 | contentPadding = PaddingValues(0.dp), 143 | interactionSource = interactionSource 144 | ) { 145 | content() 146 | } 147 | } 148 | 149 | @Immutable 150 | data class PadButtonColors( 151 | val content: Color, 152 | val background: Color 153 | ) { 154 | companion object { 155 | val Default = PadButtonColors( 156 | content = Color.Unspecified, 157 | background = Color.Unspecified 158 | ) 159 | } 160 | } 161 | 162 | val LocalPadButtonColors = staticCompositionLocalOf { 163 | PadButtonColors.Default 164 | } 165 | 166 | @Composable 167 | fun PadButtonTheme( 168 | padButtonColors: PadButtonColors = PadButtonColors.Default, 169 | content: @Composable () -> Unit 170 | ) { 171 | CompositionLocalProvider( 172 | LocalPadButtonColors provides padButtonColors, 173 | content = content 174 | ) 175 | } 176 | 177 | object PadButtonTheme { 178 | val colors: PadButtonColors 179 | @Composable 180 | get() = LocalPadButtonColors.current 181 | } 182 | 183 | @ButtonPreviews 184 | @Composable 185 | fun LargeTextPadButtonPreview() { 186 | GridPadExampleTheme { 187 | LargeTextPadButton("1", {}) 188 | } 189 | } 190 | 191 | @ButtonPreviews 192 | @Composable 193 | fun MediumPadButtonPreview() { 194 | GridPadExampleTheme { 195 | MediumTextPadButton("2", {}) 196 | } 197 | } 198 | 199 | @ButtonPreviews 200 | @Composable 201 | fun SmallPadButtonPreview() { 202 | GridPadExampleTheme { 203 | SmallTextPadButton("3", {}) 204 | } 205 | } 206 | 207 | @ButtonPreviews 208 | @Composable 209 | fun IconPadButtonPreview() { 210 | GridPadExampleTheme { 211 | IconPadButton(Icons.Default.Backspace, {}) 212 | } 213 | } 214 | 215 | @Preview( 216 | name = "Large 128", 217 | group = "PadButton", 218 | widthDp = 128, 219 | heightDp = 128, 220 | ) 221 | @Preview( 222 | name = "Medium 86", 223 | group = "PadButton", 224 | widthDp = 86, 225 | heightDp = 86, 226 | ) 227 | @Preview( 228 | name = "Medium Horizontal 128x86", 229 | group = "PadButton", 230 | widthDp = 128, 231 | heightDp = 86, 232 | ) 233 | @Preview( 234 | name = "Medium Vertical 86x128", 235 | group = "PadButton", 236 | widthDp = 86, 237 | heightDp = 128, 238 | ) 239 | annotation class ButtonPreviews -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/PinPad.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.material.icons.Icons 28 | import androidx.compose.material.icons.filled.Backspace 29 | import androidx.compose.runtime.Composable 30 | import androidx.compose.runtime.CompositionLocalProvider 31 | import androidx.compose.runtime.Immutable 32 | import androidx.compose.runtime.staticCompositionLocalOf 33 | import androidx.compose.ui.Modifier 34 | import androidx.compose.ui.graphics.Color 35 | import androidx.compose.ui.tooling.preview.Preview 36 | import com.touchlane.gridpad.GridPad 37 | import com.touchlane.gridpad.GridPadCells 38 | import com.touchlane.gridpad.GridPadPlacementPolicy 39 | import com.touchlane.gridpad.GridPadPlacementPolicy.VerticalDirection.BOTTOM_TOP 40 | import com.touchlane.gridpad.example.ui.theme.GridPadExampleTheme 41 | 42 | @Composable 43 | fun PinPad(modifier: Modifier = Modifier, onClick: (action: Char) -> Unit = {}) { 44 | PadButtonTheme( 45 | padButtonColors = PadButtonColors( 46 | content = PinPadTheme.colors.content, 47 | background = PinPadTheme.colors.background 48 | ) 49 | ) { 50 | GridPad( 51 | cells = GridPadCells.Builder(rowCount = 4, columnCount = 3).build(), 52 | placementPolicy = GridPadPlacementPolicy(verticalDirection = BOTTOM_TOP), 53 | modifier = modifier 54 | ) { 55 | //row 3 56 | item(row = 3, column = 1) { 57 | LargeTextPadButton(text = "0", onClick = { onClick('0') }) 58 | } 59 | item { 60 | PadButtonTheme( 61 | padButtonColors = PadButtonColors( 62 | content = PinPadTheme.colors.removeContent, 63 | background = PinPadTheme.colors.background 64 | ) 65 | ) { 66 | IconPadButton(icon = Icons.Default.Backspace, onClick = { onClick('r') }) 67 | } 68 | } 69 | //row 2 70 | item { 71 | LargeTextPadButton(text = "1", onClick = { onClick('1') }) 72 | } 73 | item { 74 | LargeTextPadButton(text = "2", onClick = { onClick('2') }) 75 | } 76 | item { 77 | LargeTextPadButton(text = "3", onClick = { onClick('3') }) 78 | } 79 | //row 1 80 | item { 81 | LargeTextPadButton(text = "4", onClick = { onClick('4') }) 82 | } 83 | item { 84 | LargeTextPadButton(text = "5", onClick = { onClick('5') }) 85 | } 86 | item { 87 | LargeTextPadButton(text = "6", onClick = { onClick('6') }) 88 | } 89 | //row 0 90 | item { 91 | LargeTextPadButton(text = "7", onClick = { onClick('7') }) 92 | } 93 | item { 94 | LargeTextPadButton(text = "8", onClick = { onClick('8') }) 95 | } 96 | item { 97 | LargeTextPadButton(text = "9", onClick = { onClick('9') }) 98 | } 99 | } 100 | } 101 | } 102 | 103 | @Immutable 104 | data class PinPadColors( 105 | val content: Color, 106 | val removeContent: Color, 107 | val background: Color 108 | ) { 109 | companion object { 110 | val Default = PinPadColors( 111 | content = Color.Unspecified, 112 | removeContent = Color.Unspecified, 113 | background = Color.Unspecified 114 | ) 115 | } 116 | } 117 | 118 | val LocalPinPadColors = staticCompositionLocalOf { 119 | PinPadColors.Default 120 | } 121 | 122 | @Composable 123 | fun PinPadTheme( 124 | colors: PinPadColors = PinPadColors.Default, 125 | content: @Composable () -> Unit 126 | ) { 127 | CompositionLocalProvider( 128 | LocalPinPadColors provides colors, 129 | content = content 130 | ) 131 | } 132 | 133 | object PinPadTheme { 134 | val colors: PinPadColors 135 | @Composable 136 | get() = LocalPinPadColors.current 137 | } 138 | 139 | @Preview(showBackground = true, widthDp = 300, heightDp = 300) 140 | @Preview(showBackground = true, widthDp = 300, heightDp = 400) 141 | @Composable 142 | fun PinPadPreview() { 143 | GridPadExampleTheme { 144 | PinPad() 145 | } 146 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/SimpleCalculatorPad.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.material.icons.Icons 28 | import androidx.compose.material.icons.filled.Backspace 29 | import androidx.compose.runtime.Composable 30 | import androidx.compose.runtime.CompositionLocalProvider 31 | import androidx.compose.runtime.Immutable 32 | import androidx.compose.runtime.staticCompositionLocalOf 33 | import androidx.compose.ui.Modifier 34 | import androidx.compose.ui.graphics.Color 35 | import androidx.compose.ui.tooling.preview.Preview 36 | import com.touchlane.gridpad.GridPad 37 | import com.touchlane.gridpad.GridPadCells 38 | import com.touchlane.gridpad.example.ui.theme.GridPadExampleTheme 39 | 40 | @Composable 41 | fun SimpleCalculatorPad(modifier: Modifier = Modifier) { 42 | PadButtonTheme( 43 | padButtonColors = PadButtonColors( 44 | content = SimpleCalculatorPadTheme.colors.content, 45 | background = SimpleCalculatorPadTheme.colors.background 46 | ) 47 | ) { 48 | GridPad( 49 | cells = GridPadCells.Builder(rowCount = 5, columnCount = 4).build(), 50 | modifier = modifier 51 | ) { 52 | //row 0 53 | item { 54 | RemoveTheme { 55 | LargeTextPadButton(text = "C", onClick = {}) 56 | } 57 | } 58 | item { 59 | ActionTheme { 60 | LargeTextPadButton(text = "÷", onClick = {}) 61 | } 62 | } 63 | item { 64 | ActionTheme { 65 | LargeTextPadButton(text = "×", onClick = {}) 66 | } 67 | } 68 | item { 69 | RemoveTheme { 70 | IconPadButton(icon = Icons.Default.Backspace, onClick = {}) 71 | } 72 | } 73 | //row 1 74 | item { 75 | LargeTextPadButton(text = "7", onClick = {}) 76 | } 77 | item { 78 | LargeTextPadButton(text = "8", onClick = {}) 79 | } 80 | item { 81 | LargeTextPadButton(text = "9", onClick = {}) 82 | } 83 | item { 84 | ActionTheme { 85 | LargeTextPadButton(text = "−", onClick = {}) 86 | } 87 | } 88 | //row 1 89 | item { 90 | LargeTextPadButton(text = "4", onClick = {}) 91 | } 92 | item { 93 | LargeTextPadButton(text = "5", onClick = {}) 94 | } 95 | item { 96 | LargeTextPadButton(text = "6", onClick = {}) 97 | } 98 | item { 99 | ActionTheme { 100 | LargeTextPadButton(text = "+", onClick = {}) 101 | } 102 | } 103 | //row 2 104 | item { 105 | LargeTextPadButton(text = "1", onClick = {}) 106 | } 107 | item { 108 | LargeTextPadButton(text = "2", onClick = {}) 109 | } 110 | item { 111 | LargeTextPadButton(text = "3", onClick = {}) 112 | } 113 | item(rowSpan = 2) { 114 | ActionTheme { 115 | LargeTextPadButton(text = "=", onClick = {}) 116 | } 117 | } 118 | //row 3 119 | item(row = 4, column = 0) { 120 | LargeTextPadButton(text = ".", onClick = {}) 121 | } 122 | item(columnSpan = 2) { 123 | LargeTextPadButton(text = "0", onClick = {}) 124 | } 125 | } 126 | } 127 | } 128 | 129 | @Composable 130 | private fun RemoveTheme(content: @Composable () -> Unit) { 131 | PadButtonTheme( 132 | padButtonColors = PadButtonColors( 133 | content = SimpleCalculatorPadTheme.colors.removeContent, 134 | background = SimpleCalculatorPadTheme.colors.background 135 | ) 136 | ) { 137 | content() 138 | } 139 | } 140 | 141 | @Composable 142 | private fun ActionTheme(content: @Composable () -> Unit) { 143 | PadButtonTheme( 144 | padButtonColors = PadButtonColors( 145 | content = SimpleCalculatorPadTheme.colors.actionsContent, 146 | background = SimpleCalculatorPadTheme.colors.background 147 | ) 148 | ) { 149 | content() 150 | } 151 | } 152 | 153 | @Immutable 154 | data class SimpleCalculatorPadColors( 155 | val content: Color, 156 | val removeContent: Color, 157 | val actionsContent: Color, 158 | val background: Color 159 | ) { 160 | companion object { 161 | val Default = SimpleCalculatorPadColors( 162 | content = Color.Unspecified, 163 | removeContent = Color.Unspecified, 164 | actionsContent = Color.Unspecified, 165 | background = Color.Unspecified 166 | ) 167 | } 168 | } 169 | 170 | val LocalSimpleCalculatorPadColors = staticCompositionLocalOf { 171 | SimpleCalculatorPadColors.Default 172 | } 173 | 174 | @Composable 175 | fun SimpleCalculatorPadTheme( 176 | colors: SimpleCalculatorPadColors = SimpleCalculatorPadColors.Default, 177 | content: @Composable () -> Unit 178 | ) { 179 | CompositionLocalProvider( 180 | LocalSimpleCalculatorPadColors provides colors, 181 | content = content 182 | ) 183 | } 184 | 185 | object SimpleCalculatorPadTheme { 186 | val colors: SimpleCalculatorPadColors 187 | @Composable 188 | get() = LocalSimpleCalculatorPadColors.current 189 | } 190 | 191 | @Preview(showBackground = true, widthDp = 400, heightDp = 500) 192 | @Preview(showBackground = true, widthDp = 400, heightDp = 400) 193 | @Composable 194 | fun SimpleCalculatorPadPreview() { 195 | GridPadExampleTheme { 196 | SimpleCalculatorPad() 197 | } 198 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/component/SimplePriorityCalculatorPad.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.component 26 | 27 | import androidx.compose.material.icons.Icons 28 | import androidx.compose.material.icons.filled.Backspace 29 | import androidx.compose.runtime.Composable 30 | import androidx.compose.runtime.CompositionLocalProvider 31 | import androidx.compose.runtime.Immutable 32 | import androidx.compose.runtime.staticCompositionLocalOf 33 | import androidx.compose.ui.Modifier 34 | import androidx.compose.ui.graphics.Color 35 | import androidx.compose.ui.tooling.preview.Preview 36 | import androidx.compose.ui.unit.dp 37 | import com.touchlane.gridpad.GridPad 38 | import com.touchlane.gridpad.GridPadCellSize 39 | import com.touchlane.gridpad.GridPadCells 40 | import com.touchlane.gridpad.example.ui.theme.GridPadExampleTheme 41 | 42 | @Composable 43 | fun SimplePriorityCalculatorPad(modifier: Modifier = Modifier) { 44 | PadButtonTheme( 45 | padButtonColors = PadButtonColors( 46 | content = SimplePriorityCalculatorPadTheme.colors.content, 47 | background = SimplePriorityCalculatorPadTheme.colors.background 48 | ) 49 | ) { 50 | GridPad( 51 | cells = GridPadCells.Builder(rowCount = 5, columnCount = 5) 52 | .rowSize(0, GridPadCellSize.Fixed(64.dp)).build(), modifier = modifier 53 | ) { 54 | //row 0 55 | item { 56 | RemoveTheme { 57 | MediumTextPadButton(text = "C", onClick = {}) 58 | } 59 | } 60 | item(columnSpan = 2) { 61 | ActionTheme { 62 | MediumTextPadButton(text = "(", onClick = {}) 63 | } 64 | } 65 | item(columnSpan = 2) { 66 | ActionTheme { 67 | MediumTextPadButton(text = ")", onClick = {}) 68 | } 69 | } 70 | //row 1 71 | item { 72 | LargeTextPadButton(text = "7", onClick = {}) 73 | } 74 | item { 75 | LargeTextPadButton(text = "8", onClick = {}) 76 | } 77 | item { 78 | LargeTextPadButton(text = "9", onClick = {}) 79 | } 80 | item { 81 | ActionTheme { 82 | LargeTextPadButton(text = "×", onClick = {}) 83 | } 84 | } 85 | item { 86 | ActionTheme { 87 | LargeTextPadButton(text = "÷", onClick = {}) 88 | } 89 | } 90 | //row 2 91 | item { 92 | LargeTextPadButton(text = "4", onClick = {}) 93 | } 94 | item { 95 | LargeTextPadButton(text = "5", onClick = {}) 96 | } 97 | item { 98 | LargeTextPadButton(text = "6", onClick = {}) 99 | } 100 | item(rowSpan = 2) { 101 | ActionTheme { 102 | LargeTextPadButton(text = "−", onClick = {}) 103 | } 104 | } 105 | item(rowSpan = 2) { 106 | ActionTheme { 107 | LargeTextPadButton(text = "+", onClick = {}) 108 | } 109 | } 110 | //row 3 111 | item(row = 3, column = 0) { 112 | LargeTextPadButton(text = "1", onClick = {}) 113 | } 114 | item { 115 | LargeTextPadButton(text = "2", onClick = {}) 116 | } 117 | item { 118 | LargeTextPadButton(text = "3", onClick = {}) 119 | } 120 | //row 4 121 | item(row = 4, column = 0) { 122 | LargeTextPadButton(text = "0", onClick = {}) 123 | } 124 | item { 125 | LargeTextPadButton(text = ".", onClick = {}) 126 | } 127 | item { 128 | RemoveTheme { 129 | IconPadButton(icon = Icons.Default.Backspace, onClick = {}) 130 | } 131 | } 132 | item(columnSpan = 2) { 133 | ActionTheme { 134 | LargeTextPadButton(text = "=", onClick = {}) 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | @Composable 142 | private fun RemoveTheme(content: @Composable () -> Unit) { 143 | PadButtonTheme( 144 | padButtonColors = PadButtonColors( 145 | content = SimplePriorityCalculatorPadTheme.colors.content, 146 | background = SimplePriorityCalculatorPadTheme.colors.removeBackground 147 | ) 148 | ) { 149 | content() 150 | } 151 | } 152 | 153 | @Composable 154 | private fun ActionTheme(content: @Composable () -> Unit) { 155 | PadButtonTheme( 156 | padButtonColors = PadButtonColors( 157 | content = SimplePriorityCalculatorPadTheme.colors.content, 158 | background = SimplePriorityCalculatorPadTheme.colors.actionsBackground 159 | ) 160 | ) { 161 | content() 162 | } 163 | } 164 | 165 | @Immutable 166 | data class SimplePriorityCalculatorPadColors( 167 | val content: Color, 168 | val background: Color, 169 | val removeBackground: Color, 170 | val actionsBackground: Color 171 | ) { 172 | companion object { 173 | val Default = SimplePriorityCalculatorPadColors( 174 | content = Color.Unspecified, 175 | background = Color.Unspecified, 176 | removeBackground = Color.Unspecified, 177 | actionsBackground = Color.Unspecified 178 | ) 179 | } 180 | } 181 | 182 | val LocalSimplePriorityCalculatorPadColors = staticCompositionLocalOf { 183 | SimplePriorityCalculatorPadColors.Default 184 | } 185 | 186 | @Composable 187 | fun SimplePriorityCalculatorPadTheme( 188 | colors: SimplePriorityCalculatorPadColors = SimplePriorityCalculatorPadColors.Default, 189 | content: @Composable () -> Unit 190 | ) { 191 | CompositionLocalProvider( 192 | LocalSimplePriorityCalculatorPadColors provides colors, content = content 193 | ) 194 | } 195 | 196 | object SimplePriorityCalculatorPadTheme { 197 | val colors: SimplePriorityCalculatorPadColors 198 | @Composable get() = LocalSimplePriorityCalculatorPadColors.current 199 | } 200 | 201 | @Preview(showBackground = true, widthDp = 500, heightDp = 600) 202 | @Preview(showBackground = true, widthDp = 500, heightDp = 400) 203 | @Composable 204 | fun SimplePriorityCalculatorPadPreview() { 205 | GridPadExampleTheme { 206 | SimplePriorityCalculatorPad() 207 | } 208 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.theme 26 | 27 | import androidx.compose.ui.graphics.Color 28 | 29 | val HeatWave = Color(0xFFFF7900) 30 | val BarneyPurple = Color(0xFF990099) 31 | val AswadBlack = Color(0xFF16181c) 32 | val AndreaBlue = Color(0xFF4175e2) 33 | 34 | val White = Color(0xFFFFFFFF) 35 | 36 | val BabyBlueEyes = Color(0XFF9CCAFF) 37 | val LightSteelBlue = Color(0XFFBAC8DB) 38 | val Melon = Color(0XFFFFB4A5) -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.theme 26 | 27 | import android.app.Activity 28 | import androidx.compose.material3.MaterialTheme 29 | import androidx.compose.material3.darkColorScheme 30 | import androidx.compose.runtime.Composable 31 | import androidx.compose.runtime.SideEffect 32 | import androidx.compose.ui.graphics.toArgb 33 | import androidx.compose.ui.platform.LocalView 34 | import androidx.core.view.WindowCompat 35 | 36 | private val DarkColorScheme = darkColorScheme( 37 | background = AswadBlack, 38 | surface = AswadBlack, 39 | primary = BabyBlueEyes, 40 | secondary = LightSteelBlue, 41 | tertiary = Melon 42 | ) 43 | 44 | @Composable 45 | fun GridPadExampleTheme( 46 | content: @Composable () -> Unit 47 | ) { 48 | val colorScheme = DarkColorScheme 49 | val view = LocalView.current 50 | if (!view.isInEditMode) { 51 | SideEffect { 52 | val window = (view.context as Activity).window 53 | window.statusBarColor = colorScheme.primary.toArgb() 54 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true 55 | } 56 | } 57 | 58 | MaterialTheme( 59 | colorScheme = colorScheme, 60 | typography = Typography, 61 | content = content 62 | ) 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/touchlane/gridpad/example/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Touchlane LLC tech@touchlane.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.touchlane.gridpad.example.ui.theme 26 | 27 | import androidx.compose.material3.Typography 28 | import androidx.compose.ui.text.TextStyle 29 | import androidx.compose.ui.text.font.FontFamily 30 | import androidx.compose.ui.text.font.FontWeight 31 | import androidx.compose.ui.unit.sp 32 | 33 | // Set of Material typography styles to start with 34 | val Typography = Typography( 35 | bodyLarge = TextStyle( 36 | fontFamily = FontFamily.Default, 37 | fontWeight = FontWeight.Normal, 38 | fontSize = 16.sp, 39 | lineHeight = 24.sp, 40 | letterSpacing = 0.5.sp 41 | ) 42 | /* Other default text styles to override 43 | titleLarge = TextStyle( 44 | fontFamily = FontFamily.Default, 45 | fontWeight = FontWeight.Normal, 46 | fontSize = 22.sp, 47 | lineHeight = 28.sp, 48 | letterSpacing = 0.sp 49 | ), 50 | labelSmall = TextStyle( 51 | fontFamily = FontFamily.Default, 52 | fontWeight = FontWeight.Medium, 53 | fontSize = 11.sp, 54 | lineHeight = 16.sp, 55 | letterSpacing = 0.5.sp 56 | ) 57 | */ 58 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 31 | 32 | 33 | 39 | 42 | 45 | 46 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 31 | 34 | 39 | 44 | 49 | 54 | 59 | 64 | 69 | 74 | 79 | 84 | 89 | 94 | 99 | 104 | 109 | 114 | 119 | 124 | 129 | 134 | 139 | 144 | 149 | 154 | 159 | 164 | 169 | 174 | 179 | 184 | 189 | 194 | 195 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touchlane/gridpad-android/5ce0dcbe08cfe4cc3949b6c25454b4983e9f75c5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | #FFBB86FC 28 | #FF6200EE 29 | #FF3700B3 30 | #FF03DAC5 31 | #FF018786 32 | #FF000000 33 | #FFFFFFFF 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | GridPad Example 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 |