├── README.md ├── assets ├── banner.png └── spin-animation.gif ├── build.gradle.kts ├── composeApp ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── chaintech │ │ └── networkapp │ │ ├── App.android.kt │ │ └── theme │ │ └── Theme.android.kt │ ├── commonMain │ ├── composeResources │ │ ├── drawable │ │ │ ├── icn_background.png │ │ │ ├── icn_center.xml │ │ │ ├── icn_coin.png │ │ │ ├── icn_gift.png │ │ │ ├── icn_jackpot.png │ │ │ ├── icn_sad.png │ │ │ ├── icn_tick.xml │ │ │ ├── icn_ticket.png │ │ │ └── icn_wheel_bg.xml │ │ ├── font │ │ │ └── IndieFlower-Regular.ttf │ │ └── values │ │ │ └── strings.xml │ └── kotlin │ │ └── chaintech │ │ └── networkapp │ │ ├── App.kt │ │ ├── theme │ │ ├── Color.kt │ │ └── Theme.kt │ │ ├── ui │ │ ├── SpinWheel.kt │ │ ├── SpinWheelComponent.kt │ │ ├── SpinWheelItem.kt │ │ ├── SpinWheelSlice.kt │ │ └── SpinWheelState.kt │ │ └── util │ │ ├── ColorUtil.kt │ │ └── util.kt │ ├── commonTest │ └── kotlin │ │ └── chaintech │ │ └── networkapp │ │ └── ComposeTest.kt │ └── iosMain │ └── kotlin │ ├── chaintech │ └── networkapp │ │ ├── App.ios.kt │ │ └── theme │ │ └── Theme.ios.kt │ └── main.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── iosApp ├── iosApp.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── iosApp │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json │ └── iosApp.swift └── settings.gradle.kts /README.md: -------------------------------------------------------------------------------- 1 | # Compose Multiplatform Shake Detection 2 | 3 | CMPShakeDetection is a powerful library designed to bring shake detection capabilities to your Compose Multiplatform applications. Whether you're looking to add fun interactions, hidden features, or intuitive controls, CMPShakeDetection makes it easy to implement shake detection across Android and iOS platforms. 4 | 5 | CMPShakeDetection-git-banner 6 | 7 | ## Features 8 | **Cross-Platform:** Works seamlessly with Compose Multiplatform. 9 | 10 | **Easy Integration:** Simple setup and usage. 11 | 12 | **Highly Customizable:** Define custom actions on shake events. 13 | 14 | **Lightweight:** Minimal impact on app performance. 15 | 16 | ## Installation 17 | 18 | Add the following dependency to your `build.gradle.kts` file: 19 | 20 | ```kotlin 21 | commonMain.dependencies { 22 | implementation("network.chaintech:compose-multiplatform-shake-detection:1.0.0") 23 | } 24 | ``` 25 | ### iOS Setup 26 | Add the following key to your Info.plist to request permission for motion data: 27 | 28 | ```kotlin 29 | NSMotionUsageDescription 30 | This app uses motion data to detect shakes. 31 | ``` 32 | ## Usage 33 | 34 | **Initialize the Shake Detector** 35 | 36 | Create an instance of ShakeDetector in your shared code using rememberShakeDetector(): 37 | 38 | ```kotlin 39 | val shakeDetector = rememberShakeDetector() 40 | ``` 41 | 42 | **Start Detecting Shakes** 43 | 44 | Start detecting shakes in a LaunchedEffect block: 45 | 46 | ```kotlin 47 | LaunchedEffect(Unit) { 48 | shakeDetector.start() 49 | } 50 | ``` 51 | 52 | **Handle Shake Events** 53 | 54 | Use the onShake callback to define what happens when a shake is detected: 55 | 56 | ```kotlin 57 | shakeDetector.onShake { 58 | // Handle Shake Event 59 | } 60 | ``` 61 | 62 | **Stop Detecting Shakes** 63 | 64 | Ensure that shake detection is stopped when the Composable is disposed by using DisposableEffect: 65 | 66 | ```kotlin 67 | DisposableEffect(Unit) { 68 | onDispose { 69 | shakeDetector.stop() 70 | } 71 | } 72 | ``` 73 | ## Sample Example 74 | 75 | ![Shake-and-spin-animation](./assets/spin-animation.gif) 76 | 77 | 78 | ```kotlin 79 | @Composable 80 | fun SpinWheel() { 81 | var isSpinning = false 82 | val shakeDetector = rememberShakeDetector() 83 | // Start detecting shakes 84 | LaunchedEffect(Unit) { 85 | shakeDetector.start() 86 | } 87 | shakeDetector.onShake { 88 | if(!isSpinning) { 89 | isSpinning = true 90 | spinState.launchInfinite() 91 | 92 | MainScope().launch { 93 | delay(timeMillis = Random.nextLong(5000, 9000)) 94 | spinState.stoppingWheel(Random.nextInt(0, 6)) 95 | isSpinning = false 96 | } 97 | } 98 | } 99 | 100 | // Stop detecting shakes when the composable is disposed 101 | DisposableEffect(Unit) { 102 | onDispose { 103 | shakeDetector.stop() 104 | } 105 | } 106 | 107 | Box( 108 | modifier = Modifier 109 | .fillMaxSize() 110 | ) { 111 | Image( 112 | modifier = Modifier.fillMaxSize(), 113 | painter = painterResource(Res.drawable.icn_background), 114 | contentScale = ContentScale.FillBounds, 115 | contentDescription = null, 116 | ) 117 | 118 | Column( 119 | modifier = Modifier 120 | .fillMaxSize() 121 | .windowInsetsPadding(WindowInsets.safeDrawing), 122 | horizontalAlignment = Alignment.CenterHorizontally 123 | ) { 124 | Spacer(modifier = Modifier.size(50.dp)) 125 | 126 | Image( 127 | modifier = Modifier.width(303.dp).height(144.dp), 128 | painter = painterResource(Res.drawable.icn_jackpot), 129 | contentScale = ContentScale.Crop, 130 | contentDescription = null, 131 | ) 132 | 133 | Spacer(modifier = Modifier.size(30.dp)) 134 | 135 | Box( 136 | modifier = Modifier 137 | .fillMaxWidth() 138 | .padding(horizontal = 30.dp) 139 | .aspectRatio(1f) 140 | ) { 141 | SpinWheelComponent(spinState) 142 | } 143 | Spacer(modifier = Modifier.size(20.dp)) 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | 150 | ## Detailed Explanation 151 | - For a detailed explanation and a comprehensive guide, please read the [Medium blog post.](https://medium.com/mobile-innovation-network/shake-it-up-with-cmpshakedetection-integrating-fun-in-compose-multiplatform-79bc360c11e7) 152 | - Followus On [Linkedin](https://www.linkedin.com/showcase/mobile-innovation-network) 153 | -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/assets/banner.png -------------------------------------------------------------------------------- /assets/spin-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/assets/spin-animation.gif -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.multiplatform).apply(false) 3 | alias(libs.plugins.compose.compiler).apply(false) 4 | alias(libs.plugins.compose).apply(false) 5 | alias(libs.plugins.android.application).apply(false) 6 | alias(libs.plugins.android.library).apply(false) 7 | alias(libs.plugins.mavenPublish).apply(false) 8 | } 9 | -------------------------------------------------------------------------------- /composeApp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.ExperimentalComposeLibrary 2 | import com.android.build.api.dsl.ManagedVirtualDevice 3 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 4 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 5 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree 6 | 7 | plugins { 8 | alias(libs.plugins.multiplatform) 9 | alias(libs.plugins.compose.compiler) 10 | alias(libs.plugins.compose) 11 | alias(libs.plugins.android.application) 12 | } 13 | 14 | kotlin { 15 | androidTarget { 16 | compilations.all { 17 | compileTaskProvider { 18 | compilerOptions { 19 | jvmTarget.set(JvmTarget.JVM_1_8) 20 | freeCompilerArgs.add("-Xjdk-release=${JavaVersion.VERSION_1_8}") 21 | } 22 | } 23 | } 24 | //https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-test.html 25 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 26 | instrumentedTestVariant { 27 | sourceSetTree.set(KotlinSourceSetTree.test) 28 | dependencies { 29 | debugImplementation(libs.androidx.testManifest) 30 | implementation(libs.androidx.junit4) 31 | } 32 | } 33 | } 34 | 35 | listOf( 36 | iosX64(), 37 | iosArm64(), 38 | iosSimulatorArm64() 39 | ).forEach { 40 | it.binaries.framework { 41 | baseName = "ComposeApp" 42 | isStatic = true 43 | } 44 | } 45 | 46 | sourceSets { 47 | commonMain.dependencies { 48 | implementation(compose.runtime) 49 | implementation(compose.foundation) 50 | implementation(compose.material3) 51 | implementation(compose.components.resources) 52 | implementation(compose.components.uiToolingPreview) 53 | implementation("network.chaintech:compose-multiplatform-shake-detection:1.0.0") 54 | } 55 | 56 | commonTest.dependencies { 57 | implementation(kotlin("test")) 58 | @OptIn(ExperimentalComposeLibrary::class) 59 | implementation(compose.uiTest) 60 | } 61 | 62 | androidMain.dependencies { 63 | implementation(compose.uiTooling) 64 | implementation(libs.androidx.activityCompose) 65 | } 66 | 67 | iosMain.dependencies { 68 | } 69 | 70 | } 71 | } 72 | 73 | android { 74 | namespace = "chaintech.networkapp" 75 | compileSdk = 34 76 | 77 | defaultConfig { 78 | minSdk = 24 79 | targetSdk = 34 80 | 81 | applicationId = "chaintech.networkapp.androidApp" 82 | versionCode = 1 83 | versionName = "1.0.0" 84 | 85 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 86 | } 87 | sourceSets["main"].apply { 88 | manifest.srcFile("src/androidMain/AndroidManifest.xml") 89 | res.srcDirs("src/androidMain/res") 90 | } 91 | //https://developer.android.com/studio/test/gradle-managed-devices 92 | @Suppress("UnstableApiUsage") 93 | testOptions { 94 | managedDevices.devices { 95 | maybeCreate("pixel5").apply { 96 | device = "Pixel 5" 97 | apiLevel = 34 98 | systemImageSource = "aosp" 99 | } 100 | } 101 | } 102 | compileOptions { 103 | sourceCompatibility = JavaVersion.VERSION_1_8 104 | targetCompatibility = JavaVersion.VERSION_1_8 105 | } 106 | buildFeatures { 107 | //enables a Compose tooling support in the AndroidStudio 108 | compose = true 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /composeApp/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /composeApp/src/androidMain/kotlin/chaintech/networkapp/App.android.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp 2 | 3 | import android.app.Application 4 | import android.content.Intent 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import androidx.activity.ComponentActivity 8 | import androidx.activity.compose.setContent 9 | import androidx.activity.enableEdgeToEdge 10 | 11 | class AndroidApp : Application() { 12 | companion object { 13 | lateinit var INSTANCE: AndroidApp 14 | } 15 | 16 | override fun onCreate() { 17 | super.onCreate() 18 | INSTANCE = this 19 | } 20 | } 21 | 22 | class AppActivity : ComponentActivity() { 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | enableEdgeToEdge() 26 | setContent { App() } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /composeApp/src/androidMain/kotlin/chaintech/networkapp/theme/Theme.android.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.theme 2 | 3 | import android.app.Activity 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.LaunchedEffect 6 | import androidx.compose.ui.platform.LocalView 7 | import androidx.core.view.WindowInsetsControllerCompat 8 | 9 | @Composable 10 | internal actual fun SystemAppearance(isDark: Boolean) { 11 | val view = LocalView.current 12 | LaunchedEffect(isDark) { 13 | val window = (view.context as Activity).window 14 | WindowInsetsControllerCompat(window, window.decorView).apply { 15 | isAppearanceLightStatusBars = isDark 16 | isAppearanceLightNavigationBars = isDark 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_background.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_center.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 33 | 38 | 43 | 48 | 53 | 58 | 63 | 68 | 73 | 78 | 83 | 88 | 93 | 98 | 103 | 108 | 113 | 118 | 123 | 128 | 133 | 138 | 143 | 148 | 153 | 158 | 163 | 168 | 173 | 178 | 183 | 188 | 191 | 193 | 194 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 209 | 210 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 228 | 229 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 250 | 251 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 272 | 273 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 294 | 295 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 316 | 317 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 338 | 339 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 360 | 361 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 382 | 383 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 404 | 405 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 426 | 427 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 448 | 449 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 470 | 471 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 492 | 493 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 514 | 515 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 536 | 537 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 558 | 559 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 580 | 581 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 602 | 603 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 624 | 625 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 646 | 647 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 668 | 669 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 690 | 691 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 712 | 713 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 734 | 735 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 756 | 757 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 778 | 779 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 800 | 801 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 822 | 823 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 844 | 845 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 866 | 867 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 888 | 889 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 910 | 911 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 932 | 933 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 954 | 955 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 976 | 977 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 998 | 999 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1020 | 1021 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1042 | 1043 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1064 | 1065 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1086 | 1087 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1108 | 1109 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1130 | 1131 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1152 | 1153 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1174 | 1175 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1196 | 1197 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1216 | 1217 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_coin.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_gift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_gift.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_jackpot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_jackpot.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_sad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_sad.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_tick.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 12 | 15 | 18 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_ticket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/drawable/icn_ticket.png -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/icn_wheel_bg.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 42 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 61 | 63 | 64 | 70 | 71 | 72 | 73 | 74 | 75 | 80 | 82 | 83 | 89 | 90 | 91 | 92 | 93 | 94 | 99 | 101 | 102 | 108 | 109 | 110 | 111 | 112 | 113 | 115 | 116 | 122 | 123 | 124 | 125 | 126 | 127 | 129 | 130 | 136 | 137 | 138 | 139 | 140 | 141 | 143 | 144 | 150 | 151 | 152 | 153 | 154 | 155 | 157 | 158 | 164 | 165 | 166 | 167 | 168 | 169 | 171 | 172 | 178 | 179 | 180 | 181 | 182 | 183 | 185 | 186 | 192 | 193 | 194 | 195 | 196 | 197 | 199 | 200 | 206 | 207 | 208 | 209 | 210 | 211 | 213 | 214 | 220 | 221 | 222 | 223 | 224 | 225 | 227 | 228 | 234 | 235 | 236 | 237 | 238 | 239 | 241 | 242 | 248 | 249 | 250 | 251 | 252 | 253 | 255 | 256 | 262 | 263 | 264 | 265 | 266 | 267 | 269 | 270 | 276 | 277 | 278 | 279 | 280 | 281 | 283 | 284 | 290 | 291 | 292 | 293 | 294 | 295 | 297 | 298 | 304 | 305 | 306 | 307 | 308 | 309 | 311 | 312 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/font/IndieFlower-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/composeApp/src/commonMain/composeResources/font/IndieFlower-Regular.ttf -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Cyclone 3 | Open github 4 | Run 5 | Stop 6 | Theme 7 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/App.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.WindowInsets 8 | import androidx.compose.foundation.layout.aspectRatio 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.height 12 | import androidx.compose.foundation.layout.offset 13 | import androidx.compose.foundation.layout.padding 14 | import androidx.compose.foundation.layout.safeDrawing 15 | import androidx.compose.foundation.layout.size 16 | import androidx.compose.foundation.layout.width 17 | import androidx.compose.foundation.layout.windowInsetsPadding 18 | import androidx.compose.material3.Text 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.DisposableEffect 21 | import androidx.compose.runtime.LaunchedEffect 22 | import androidx.compose.runtime.remember 23 | import androidx.compose.ui.Alignment 24 | import androidx.compose.ui.Modifier 25 | import androidx.compose.ui.graphics.Color 26 | import androidx.compose.ui.layout.ContentScale 27 | import androidx.compose.ui.text.TextStyle 28 | import androidx.compose.ui.text.font.FontWeight 29 | import androidx.compose.ui.unit.dp 30 | import androidx.compose.ui.unit.sp 31 | import chaintech.network.cmpshakedetection.rememberShakeDetector 32 | import chaintech.networkapp.theme.AppTheme 33 | import chaintech.networkapp.ui.Item 34 | import chaintech.networkapp.ui.SpinWheelComponent 35 | import chaintech.networkapp.ui.SpinWheelItem 36 | import chaintech.networkapp.ui.rememberSpinWheelState 37 | import chaintech.networkapp.util.toColor 38 | import kotlinx.coroutines.MainScope 39 | import kotlinx.coroutines.delay 40 | import kotlinx.coroutines.launch 41 | import org.jetbrains.compose.resources.painterResource 42 | import shakedetection.composeapp.generated.resources.Res 43 | import shakedetection.composeapp.generated.resources.icn_background 44 | import shakedetection.composeapp.generated.resources.icn_center 45 | import shakedetection.composeapp.generated.resources.icn_coin 46 | import shakedetection.composeapp.generated.resources.icn_gift 47 | import shakedetection.composeapp.generated.resources.icn_jackpot 48 | import shakedetection.composeapp.generated.resources.icn_sad 49 | import shakedetection.composeapp.generated.resources.icn_tick 50 | import shakedetection.composeapp.generated.resources.icn_ticket 51 | import shakedetection.composeapp.generated.resources.icn_wheel_bg 52 | import kotlin.random.Random 53 | 54 | 55 | val spinItems = listOf( 56 | Item(title = "50 Coins", image = Res.drawable.icn_coin), 57 | Item(title = "Gifts", image = Res.drawable.icn_gift), 58 | Item(title = "500 Coins", image = Res.drawable.icn_coin), 59 | Item(title = "Try Again", image = Res.drawable.icn_sad), 60 | Item(title = "100 Coins", image = Res.drawable.icn_coin), 61 | Item(title = "Ticket", image = Res.drawable.icn_ticket), 62 | ) 63 | 64 | @Composable 65 | internal fun App() = AppTheme { 66 | 67 | val colors1 = remember { 68 | listOf( 69 | "F07021", 70 | "F5C91A" 71 | ).map { it.toColor() } 72 | } 73 | 74 | val colors2 = remember { 75 | listOf( 76 | "833735", 77 | "580343" 78 | ).map { it.toColor() } 79 | } 80 | 81 | val items = remember { 82 | List(spinItems.size) { index -> 83 | val colors = if (index % 2 == 0) colors1 else colors2 84 | 85 | SpinWheelItem( 86 | colors = colors 87 | ) { 88 | Column( 89 | modifier = Modifier 90 | .fillMaxSize(), 91 | horizontalAlignment = Alignment.CenterHorizontally 92 | ) { 93 | Image( 94 | modifier = Modifier.size(55.dp), 95 | painter = painterResource(spinItems[index].image), 96 | contentScale = ContentScale.Fit, 97 | contentDescription = null, 98 | ) 99 | Text( 100 | text = spinItems[index].title, 101 | style = TextStyle(color = Color(0xFFFFFFFF), fontSize = 12.sp, fontWeight = FontWeight.SemiBold), 102 | modifier = Modifier.offset(y = -8.dp) 103 | ) 104 | } 105 | 106 | } 107 | 108 | } 109 | } 110 | 111 | val spinState = rememberSpinWheelState( 112 | items = items, 113 | backgroundImage = Res.drawable.icn_wheel_bg, 114 | centerImage = Res.drawable.icn_center, 115 | indicatorImage = Res.drawable.icn_tick, 116 | onSpinningFinished = null, 117 | ) 118 | var isSpinning = false 119 | val shakeDetector = rememberShakeDetector() 120 | // Start detecting shakes 121 | LaunchedEffect(Unit) { 122 | shakeDetector.start() 123 | } 124 | shakeDetector.onShake { 125 | if(!isSpinning) { 126 | isSpinning = true 127 | spinState.launchInfinite() 128 | 129 | MainScope().launch { 130 | delay(timeMillis = Random.nextLong(5000, 9000)) 131 | spinState.stoppingWheel(Random.nextInt(0, 6)) 132 | // delay(5000) 133 | isSpinning = false 134 | } 135 | } 136 | } 137 | 138 | // Stop detecting shakes when the composable is disposed 139 | DisposableEffect(Unit) { 140 | onDispose { 141 | shakeDetector.stop() 142 | } 143 | } 144 | 145 | Box( 146 | modifier = Modifier 147 | .fillMaxSize() 148 | ) { 149 | Image( 150 | modifier = Modifier.fillMaxSize(), 151 | painter = painterResource(Res.drawable.icn_background), 152 | contentScale = ContentScale.FillBounds, 153 | contentDescription = null, 154 | ) 155 | 156 | Column( 157 | modifier = Modifier 158 | .fillMaxSize() 159 | .windowInsetsPadding(WindowInsets.safeDrawing), 160 | horizontalAlignment = Alignment.CenterHorizontally 161 | ) { 162 | Spacer(modifier = Modifier.size(50.dp)) 163 | 164 | Image( 165 | modifier = Modifier.width(303.dp).height(144.dp), 166 | painter = painterResource(Res.drawable.icn_jackpot), 167 | contentScale = ContentScale.Crop, 168 | contentDescription = null, 169 | ) 170 | 171 | Spacer(modifier = Modifier.size(30.dp)) 172 | 173 | Box( 174 | modifier = Modifier 175 | .fillMaxWidth() 176 | .padding(horizontal = 30.dp) 177 | .aspectRatio(1f) 178 | ) { 179 | SpinWheelComponent(spinState) 180 | } 181 | Spacer(modifier = Modifier.size(20.dp)) 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | //generated by https://m3.material.io/theme-builder#/custom 6 | //Color palette was taken here: https://colorhunt.co/palettes/popular 7 | 8 | internal val md_theme_light_primary = Color(0xFF00687A) 9 | internal val md_theme_light_onPrimary = Color(0xFFFFFFFF) 10 | internal val md_theme_light_primaryContainer = Color(0xFFABEDFF) 11 | internal val md_theme_light_onPrimaryContainer = Color(0xFF001F26) 12 | internal val md_theme_light_secondary = Color(0xFF00696E) 13 | internal val md_theme_light_onSecondary = Color(0xFFFFFFFF) 14 | internal val md_theme_light_secondaryContainer = Color(0xFF6FF6FE) 15 | internal val md_theme_light_onSecondaryContainer = Color(0xFF002022) 16 | internal val md_theme_light_tertiary = Color(0xFF904D00) 17 | internal val md_theme_light_onTertiary = Color(0xFFFFFFFF) 18 | internal val md_theme_light_tertiaryContainer = Color(0xFFFFDCC2) 19 | internal val md_theme_light_onTertiaryContainer = Color(0xFF2E1500) 20 | internal val md_theme_light_error = Color(0xFFBA1A1A) 21 | internal val md_theme_light_errorContainer = Color(0xFFFFDAD6) 22 | internal val md_theme_light_onError = Color(0xFFFFFFFF) 23 | internal val md_theme_light_onErrorContainer = Color(0xFF410002) 24 | internal val md_theme_light_background = Color(0xFFFFFBFF) 25 | internal val md_theme_light_onBackground = Color(0xFF221B00) 26 | internal val md_theme_light_surface = Color(0xFFFFFBFF) 27 | internal val md_theme_light_onSurface = Color(0xFF221B00) 28 | internal val md_theme_light_surfaceVariant = Color(0xFFDBE4E7) 29 | internal val md_theme_light_onSurfaceVariant = Color(0xFF3F484B) 30 | internal val md_theme_light_outline = Color(0xFF70797B) 31 | internal val md_theme_light_inverseOnSurface = Color(0xFFFFF0C0) 32 | internal val md_theme_light_inverseSurface = Color(0xFF3A3000) 33 | internal val md_theme_light_inversePrimary = Color(0xFF55D6F4) 34 | internal val md_theme_light_shadow = Color(0xFF000000) 35 | internal val md_theme_light_surfaceTint = Color(0xFF00687A) 36 | internal val md_theme_light_outlineVariant = Color(0xFFBFC8CB) 37 | internal val md_theme_light_scrim = Color(0xFF000000) 38 | 39 | internal val md_theme_dark_primary = Color(0xFF55D6F4) 40 | internal val md_theme_dark_onPrimary = Color(0xFF003640) 41 | internal val md_theme_dark_primaryContainer = Color(0xFF004E5C) 42 | internal val md_theme_dark_onPrimaryContainer = Color(0xFFABEDFF) 43 | internal val md_theme_dark_secondary = Color(0xFF4CD9E2) 44 | internal val md_theme_dark_onSecondary = Color(0xFF00373A) 45 | internal val md_theme_dark_secondaryContainer = Color(0xFF004F53) 46 | internal val md_theme_dark_onSecondaryContainer = Color(0xFF6FF6FE) 47 | internal val md_theme_dark_tertiary = Color(0xFFFFB77C) 48 | internal val md_theme_dark_onTertiary = Color(0xFF4D2700) 49 | internal val md_theme_dark_tertiaryContainer = Color(0xFF6D3900) 50 | internal val md_theme_dark_onTertiaryContainer = Color(0xFFFFDCC2) 51 | internal val md_theme_dark_error = Color(0xFFFFB4AB) 52 | internal val md_theme_dark_errorContainer = Color(0xFF93000A) 53 | internal val md_theme_dark_onError = Color(0xFF690005) 54 | internal val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) 55 | internal val md_theme_dark_background = Color(0xFF221B00) 56 | internal val md_theme_dark_onBackground = Color(0xFFFFE264) 57 | internal val md_theme_dark_surface = Color(0xFF221B00) 58 | internal val md_theme_dark_onSurface = Color(0xFFFFE264) 59 | internal val md_theme_dark_surfaceVariant = Color(0xFF3F484B) 60 | internal val md_theme_dark_onSurfaceVariant = Color(0xFFBFC8CB) 61 | internal val md_theme_dark_outline = Color(0xFF899295) 62 | internal val md_theme_dark_inverseOnSurface = Color(0xFF221B00) 63 | internal val md_theme_dark_inverseSurface = Color(0xFFFFE264) 64 | internal val md_theme_dark_inversePrimary = Color(0xFF00687A) 65 | internal val md_theme_dark_shadow = Color(0xFF000000) 66 | internal val md_theme_dark_surfaceTint = Color(0xFF55D6F4) 67 | internal val md_theme_dark_outlineVariant = Color(0xFF3F484B) 68 | internal val md_theme_dark_scrim = Color(0xFF000000) 69 | 70 | 71 | internal val seed = Color(0xFF2C3639) 72 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.material3.Surface 6 | import androidx.compose.material3.darkColorScheme 7 | import androidx.compose.material3.lightColorScheme 8 | import androidx.compose.runtime.* 9 | 10 | private val LightColorScheme = lightColorScheme( 11 | primary = md_theme_light_primary, 12 | onPrimary = md_theme_light_onPrimary, 13 | primaryContainer = md_theme_light_primaryContainer, 14 | onPrimaryContainer = md_theme_light_onPrimaryContainer, 15 | secondary = md_theme_light_secondary, 16 | onSecondary = md_theme_light_onSecondary, 17 | secondaryContainer = md_theme_light_secondaryContainer, 18 | onSecondaryContainer = md_theme_light_onSecondaryContainer, 19 | tertiary = md_theme_light_tertiary, 20 | onTertiary = md_theme_light_onTertiary, 21 | tertiaryContainer = md_theme_light_tertiaryContainer, 22 | onTertiaryContainer = md_theme_light_onTertiaryContainer, 23 | error = md_theme_light_error, 24 | errorContainer = md_theme_light_errorContainer, 25 | onError = md_theme_light_onError, 26 | onErrorContainer = md_theme_light_onErrorContainer, 27 | background = md_theme_light_background, 28 | onBackground = md_theme_light_onBackground, 29 | surface = md_theme_light_surface, 30 | onSurface = md_theme_light_onSurface, 31 | surfaceVariant = md_theme_light_surfaceVariant, 32 | onSurfaceVariant = md_theme_light_onSurfaceVariant, 33 | outline = md_theme_light_outline, 34 | inverseOnSurface = md_theme_light_inverseOnSurface, 35 | inverseSurface = md_theme_light_inverseSurface, 36 | inversePrimary = md_theme_light_inversePrimary, 37 | surfaceTint = md_theme_light_surfaceTint, 38 | outlineVariant = md_theme_light_outlineVariant, 39 | scrim = md_theme_light_scrim, 40 | ) 41 | 42 | private val DarkColorScheme = darkColorScheme( 43 | primary = md_theme_dark_primary, 44 | onPrimary = md_theme_dark_onPrimary, 45 | primaryContainer = md_theme_dark_primaryContainer, 46 | onPrimaryContainer = md_theme_dark_onPrimaryContainer, 47 | secondary = md_theme_dark_secondary, 48 | onSecondary = md_theme_dark_onSecondary, 49 | secondaryContainer = md_theme_dark_secondaryContainer, 50 | onSecondaryContainer = md_theme_dark_onSecondaryContainer, 51 | tertiary = md_theme_dark_tertiary, 52 | onTertiary = md_theme_dark_onTertiary, 53 | tertiaryContainer = md_theme_dark_tertiaryContainer, 54 | onTertiaryContainer = md_theme_dark_onTertiaryContainer, 55 | error = md_theme_dark_error, 56 | errorContainer = md_theme_dark_errorContainer, 57 | onError = md_theme_dark_onError, 58 | onErrorContainer = md_theme_dark_onErrorContainer, 59 | background = md_theme_dark_background, 60 | onBackground = md_theme_dark_onBackground, 61 | surface = md_theme_dark_surface, 62 | onSurface = md_theme_dark_onSurface, 63 | surfaceVariant = md_theme_dark_surfaceVariant, 64 | onSurfaceVariant = md_theme_dark_onSurfaceVariant, 65 | outline = md_theme_dark_outline, 66 | inverseOnSurface = md_theme_dark_inverseOnSurface, 67 | inverseSurface = md_theme_dark_inverseSurface, 68 | inversePrimary = md_theme_dark_inversePrimary, 69 | surfaceTint = md_theme_dark_surfaceTint, 70 | outlineVariant = md_theme_dark_outlineVariant, 71 | scrim = md_theme_dark_scrim, 72 | ) 73 | 74 | internal val LocalThemeIsDark = compositionLocalOf { mutableStateOf(true) } 75 | 76 | @Composable 77 | internal fun AppTheme( 78 | content: @Composable() () -> Unit 79 | ) { 80 | val systemIsDark = isSystemInDarkTheme() 81 | val isDarkState = remember { mutableStateOf(systemIsDark) } 82 | CompositionLocalProvider( 83 | LocalThemeIsDark provides isDarkState 84 | ) { 85 | val isDark by isDarkState 86 | SystemAppearance(!isDark) 87 | MaterialTheme( 88 | colorScheme = if (isDark) DarkColorScheme else LightColorScheme, 89 | content = { Surface(content = content) } 90 | ) 91 | } 92 | } 93 | 94 | @Composable 95 | internal expect fun SystemAppearance(isDark: Boolean) 96 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/ui/SpinWheel.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.ui 2 | 3 | import androidx.compose.foundation.layout.BoxWithConstraints 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.draw.rotate 7 | import androidx.compose.ui.platform.LocalDensity 8 | import androidx.compose.ui.unit.min 9 | import chaintech.networkapp.util.toBrush 10 | 11 | @Composable 12 | internal fun SpinWheel( 13 | modifier: Modifier = Modifier, 14 | items: List, 15 | ) { 16 | BoxWithConstraints(modifier = modifier) { 17 | 18 | val degreesPerItems = items.getDegreesPerItem() 19 | val size = min(this.maxHeight, this.maxWidth) 20 | val brushEnd = with(LocalDensity.current) { size.toPx() / 2f } 21 | 22 | items.forEachIndexed { index, item -> 23 | SpinWheelSlice( 24 | modifier = Modifier.rotate(degrees = degreesPerItems * index), 25 | size = size, 26 | brush = item.colors.toBrush(brushEnd), 27 | degree = degreesPerItems, 28 | content = item.content 29 | ) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/ui/SpinWheelComponent.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.ui 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.BoxWithConstraints 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Spacer 8 | import androidx.compose.foundation.layout.aspectRatio 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.offset 12 | import androidx.compose.foundation.layout.size 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.graphics.graphicsLayer 17 | import androidx.compose.ui.layout.ContentScale 18 | import androidx.compose.ui.unit.dp 19 | import org.jetbrains.compose.resources.painterResource 20 | import shakedetection.composeapp.generated.resources.Res 21 | import shakedetection.composeapp.generated.resources.icn_center 22 | import shakedetection.composeapp.generated.resources.icn_tick 23 | import shakedetection.composeapp.generated.resources.icn_wheel_bg 24 | 25 | @Composable 26 | internal fun SpinWheelComponent(spinWheelState: SpinWheelState) { 27 | Box( 28 | modifier = Modifier 29 | .fillMaxWidth() 30 | .aspectRatio(1f) 31 | ) { 32 | Image( 33 | modifier = Modifier.fillMaxSize(), 34 | painter = painterResource(Res.drawable.icn_wheel_bg), 35 | contentScale = ContentScale.FillBounds, 36 | contentDescription = null, 37 | ) 38 | Column( 39 | modifier = Modifier 40 | .fillMaxSize(), 41 | horizontalAlignment = Alignment.CenterHorizontally 42 | ) { 43 | Spacer(modifier = Modifier.weight(7f)) 44 | BoxWithConstraints( 45 | modifier = Modifier 46 | .weight(86f) 47 | .aspectRatio(1f) 48 | ) { 49 | val imageSize = this.maxHeight.times(0.14f) 50 | SpinWheel(modifier = Modifier 51 | .fillMaxSize() 52 | .graphicsLayer { 53 | rotationZ = spinWheelState.rotation.value 54 | }, items = spinWheelState.items) 55 | Image( 56 | modifier = Modifier 57 | .align(Alignment.Center) 58 | .size(imageSize), 59 | painter = painterResource(Res.drawable.icn_center), 60 | contentDescription = null 61 | ) 62 | } 63 | Spacer(modifier = Modifier.weight(7f)) 64 | } 65 | Column( 66 | modifier = Modifier 67 | .fillMaxSize() 68 | .aspectRatio(1f) 69 | .offset(y = -20.dp), 70 | horizontalAlignment = Alignment.CenterHorizontally 71 | ) { 72 | Image( 73 | modifier = Modifier 74 | .weight(14f) 75 | .aspectRatio(1f), 76 | painter = painterResource(Res.drawable.icn_tick), 77 | contentDescription = null 78 | ) 79 | 80 | Spacer(modifier = Modifier.weight(62f)) 81 | } 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/ui/SpinWheelItem.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.ui 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.Stable 5 | import androidx.compose.ui.graphics.Color 6 | import org.jetbrains.compose.resources.DrawableResource 7 | 8 | @Stable 9 | data class SpinWheelItem( 10 | val colors: List, 11 | val content: @Composable () -> Unit, 12 | ) 13 | 14 | data class Item( 15 | val title: String, 16 | val image:DrawableResource 17 | ) 18 | 19 | 20 | internal fun List.getDegreesPerItem(): Float = 360f / this.size.toFloat() -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/ui/SpinWheelSlice.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.ui 2 | 3 | import androidx.compose.foundation.Canvas 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.layout.size 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.graphics.Brush 11 | import androidx.compose.ui.unit.Dp 12 | import androidx.compose.ui.unit.dp 13 | 14 | @Composable 15 | internal fun SpinWheelSlice( 16 | modifier: Modifier = Modifier, 17 | size: Dp, 18 | brush: Brush, 19 | degree: Float, 20 | content: @Composable () -> Unit, 21 | ) { 22 | Box( 23 | modifier = modifier 24 | .size(size) 25 | ) { 26 | Canvas( 27 | modifier = Modifier 28 | .size(size) 29 | ) { 30 | 31 | drawArc( 32 | brush = brush, 33 | startAngle = -90f - (degree / 2), 34 | sweepAngle = degree, 35 | useCenter = true, 36 | ) 37 | } 38 | Box(modifier = Modifier.align(Alignment.TopCenter).padding(top = 10.dp)) { 39 | content() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/ui/SpinWheelState.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.ui 2 | 3 | import androidx.compose.animation.core.Animatable 4 | import androidx.compose.animation.core.EaseOutQuad 5 | import androidx.compose.animation.core.LinearEasing 6 | import androidx.compose.animation.core.RepeatMode 7 | import androidx.compose.animation.core.infiniteRepeatable 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.Stable 11 | import androidx.compose.runtime.remember 12 | import androidx.compose.runtime.rememberCoroutineScope 13 | import chaintech.networkapp.util.getDegreeFromSection 14 | import chaintech.networkapp.util.getDegreeFromSectionWithRandom 15 | import kotlinx.coroutines.CoroutineScope 16 | import kotlinx.coroutines.launch 17 | import org.jetbrains.compose.resources.DrawableResource 18 | import kotlin.time.Duration 19 | import kotlin.time.Duration.Companion.seconds 20 | 21 | @Stable 22 | data class SpinWheelState( 23 | internal val items: List, 24 | internal val backgroundImage: DrawableResource, 25 | internal val centerImage: DrawableResource, 26 | internal val indicatorImage: DrawableResource, 27 | private val initSpinWheelSection: Int?, 28 | private val onSpinningFinished: (() -> Unit)?, 29 | private val stopDuration: Duration, 30 | private val stopNbTurn: Float, 31 | private val rotationPerSecond: Float, 32 | private val scope: CoroutineScope, 33 | ) { 34 | internal val rotation = Animatable(0f) 35 | 36 | init { 37 | initSpinWheelSection?.let { 38 | goto(it) 39 | } ?: launchInfinite() 40 | } 41 | 42 | fun stoppingWheel(sectionToStop: Int) { 43 | if (sectionToStop !in items.indices) { 44 | return 45 | } 46 | 47 | 48 | scope.launch { 49 | val destinationDegree = getDegreeFromSectionWithRandom(items, sectionToStop) 50 | 51 | rotation.animateTo( 52 | targetValue = rotation.value + (stopNbTurn * 360f) + destinationDegree + (360f - (rotation.value % 360f)), 53 | animationSpec = tween( 54 | durationMillis = stopDuration.inWholeMilliseconds.toInt(), 55 | easing = EaseOutQuad 56 | ) 57 | ) 58 | } 59 | 60 | } 61 | 62 | fun goto(section: Int) { 63 | scope.launch { 64 | if (section !in items.indices) { 65 | 66 | return@launch 67 | } 68 | val positionDegree = getDegreeFromSection(items, section) 69 | rotation.snapTo(positionDegree) 70 | } 71 | } 72 | 73 | fun launchInfinite() { 74 | scope.launch { 75 | // Infinite repeatable rotation when is playing 76 | rotation.animateTo( 77 | targetValue = rotation.value + 360f, 78 | animationSpec = infiniteRepeatable( 79 | animation = tween( 80 | durationMillis = (rotationPerSecond * 1000f).toInt(), 81 | easing = LinearEasing 82 | ), 83 | repeatMode = RepeatMode.Restart 84 | ) 85 | ) 86 | } 87 | } 88 | } 89 | 90 | @Composable 91 | fun rememberSpinWheelState( 92 | items: List, 93 | backgroundImage: DrawableResource, 94 | centerImage: DrawableResource, 95 | indicatorImage: DrawableResource, 96 | onSpinningFinished: (() -> Unit)?, 97 | initSpinWheelSection: Int? = 0, //if null then infinite 98 | stopDuration: Duration = 5.seconds, 99 | stopNbTurn: Float = 3f, 100 | rotationPerSecond: Float = 0.8f, 101 | scope: CoroutineScope = rememberCoroutineScope(), 102 | ): SpinWheelState { 103 | return remember { 104 | SpinWheelState( 105 | items = items, 106 | backgroundImage = backgroundImage, 107 | centerImage = centerImage, 108 | indicatorImage = indicatorImage, 109 | initSpinWheelSection = initSpinWheelSection, 110 | stopDuration = stopDuration, 111 | stopNbTurn = stopNbTurn, 112 | rotationPerSecond = rotationPerSecond, 113 | scope = scope, 114 | onSpinningFinished = onSpinningFinished, 115 | ) 116 | } 117 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/util/ColorUtil.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.util 2 | 3 | import androidx.compose.ui.graphics.Brush 4 | import androidx.compose.ui.graphics.Color 5 | import chaintech.networkapp.ui.SpinWheelItem 6 | import kotlin.random.Random 7 | 8 | fun String.toColor(): Color { 9 | return if (this.length == 6) { 10 | val r = this.substring(0, 2).toInt(16) 11 | val g = this.substring(2, 4).toInt(16) 12 | val b = this.substring(4, 6).toInt(16) 13 | Color(r, g, b) 14 | } else { 15 | Color.White 16 | } 17 | } 18 | 19 | fun List.toBrush(endY: Float): Brush = 20 | if (this.size == 1) { 21 | Brush.verticalGradient(colors = this) 22 | } else { 23 | val colorStops = this.mapIndexed { index, color -> 24 | val stop = if (index == 0) 0f else (index.toFloat() + 1f) / this.size.toFloat() 25 | Pair(stop, color) 26 | }.toTypedArray() 27 | Brush.verticalGradient( 28 | colorStops = colorStops, 29 | endY = endY, 30 | ) 31 | } 32 | 33 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/chaintech/networkapp/util/util.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.util 2 | 3 | import chaintech.networkapp.ui.SpinWheelItem 4 | import kotlin.random.Random 5 | 6 | fun getDegreeFromSection(items: List, section: Int): Float { 7 | val pieDegree = 360f / items.size 8 | return pieDegree * section.times(-1) 9 | } 10 | 11 | fun getDegreeFromSectionWithRandom(items: List, section: Int): Float { 12 | val pieDegree = 360f / items.size 13 | val exactDegree = pieDegree * section.times(-1) 14 | 15 | val pieReduced = pieDegree * 0.9f //to avoid stop near border 16 | val multiplier = if (Random.nextBoolean()) 1f else -1f //before or after exact degree 17 | val randomDegrees = Random.nextDouble(0.0, pieReduced / 2.0) 18 | return exactDegree + (randomDegrees.toFloat() * multiplier) 19 | } -------------------------------------------------------------------------------- /composeApp/src/commonTest/kotlin/chaintech/networkapp/ComposeTest.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.material3.Button 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.runtime.setValue 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.platform.testTag 12 | import androidx.compose.ui.test.ExperimentalTestApi 13 | import androidx.compose.ui.test.assertTextEquals 14 | import androidx.compose.ui.test.onNodeWithTag 15 | import androidx.compose.ui.test.performClick 16 | import androidx.compose.ui.test.runComposeUiTest 17 | import kotlin.test.Test 18 | 19 | @OptIn(ExperimentalTestApi::class) 20 | class ComposeTest { 21 | 22 | @Test 23 | fun simpleCheck() = runComposeUiTest { 24 | setContent { 25 | var txt by remember { mutableStateOf("Go") } 26 | Column { 27 | Text( 28 | text = txt, 29 | modifier = Modifier.testTag("t_text") 30 | ) 31 | Button( 32 | onClick = { txt += "." }, 33 | modifier = Modifier.testTag("t_button") 34 | ) { 35 | Text("click me") 36 | } 37 | } 38 | } 39 | 40 | onNodeWithTag("t_button").apply { 41 | repeat(3) { performClick() } 42 | } 43 | onNodeWithTag("t_text").assertTextEquals("Go...") 44 | } 45 | } -------------------------------------------------------------------------------- /composeApp/src/iosMain/kotlin/chaintech/networkapp/App.ios.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp 2 | 3 | import platform.Foundation.NSURL 4 | import platform.UIKit.UIApplication 5 | 6 | -------------------------------------------------------------------------------- /composeApp/src/iosMain/kotlin/chaintech/networkapp/theme/Theme.ios.kt: -------------------------------------------------------------------------------- 1 | package chaintech.networkapp.theme 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.LaunchedEffect 5 | import platform.UIKit.UIApplication 6 | import platform.UIKit.UIStatusBarStyleDarkContent 7 | import platform.UIKit.UIStatusBarStyleLightContent 8 | import platform.UIKit.setStatusBarStyle 9 | 10 | @Composable 11 | internal actual fun SystemAppearance(isDark: Boolean) { 12 | LaunchedEffect(isDark) { 13 | UIApplication.sharedApplication.setStatusBarStyle( 14 | if (isDark) UIStatusBarStyleDarkContent else UIStatusBarStyleLightContent 15 | ) 16 | } 17 | } -------------------------------------------------------------------------------- /composeApp/src/iosMain/kotlin/main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.window.ComposeUIViewController 2 | import chaintech.networkapp.App 3 | import platform.UIKit.UIViewController 4 | 5 | fun MainViewController(): UIViewController = ComposeUIViewController { App() } 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Gradle 2 | org.gradle.jvmargs=-Xmx4G -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4G" 3 | org.gradle.caching=true 4 | org.gradle.configuration-cache=true 5 | org.gradle.daemon=true 6 | org.gradle.parallel=true 7 | 8 | #Kotlin 9 | kotlin.code.style=official 10 | kotlin.js.compiler=ir 11 | 12 | #Android 13 | android.useAndroidX=true 14 | android.nonTransitiveRClass=true 15 | 16 | #Compose 17 | org.jetbrains.compose.experimental.jscanvas.enabled=true 18 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | 3 | kotlin = "2.0.0" 4 | compose = "1.6.10" 5 | agp = "8.2.2" 6 | androidx-activityCompose = "1.9.0" 7 | androidx-uiTest = "1.6.7" 8 | 9 | [libraries] 10 | 11 | androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } 12 | androidx-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-uiTest" } 13 | androidx-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-uiTest" } 14 | 15 | [plugins] 16 | 17 | multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } 18 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 19 | compose = { id = "org.jetbrains.compose", version.ref = "compose" } 20 | android-application = { id = "com.android.application", version.ref = "agp" } 21 | android-library = { id = "com.android.library", version.ref = "agp" } 22 | mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.28.0" } 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaintech-Network/CMPShakeDetection/9dbadcd284b0014fc650e578520b2c95906af445/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/sh 3 | 4 | # 5 | # Copyright © 2015-2021 the original authors. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | ############################################################################## 21 | # 22 | # Gradle start up script for POSIX generated by Gradle. 23 | # 24 | # Important for running: 25 | # 26 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 27 | # noncompliant, but you have some other compliant shell such as ksh or 28 | # bash, then to run this script, type that shell name before the whole 29 | # command line, like: 30 | # 31 | # ksh Gradle 32 | # 33 | # Busybox and similar reduced shells will NOT work, because this script 34 | # requires all of these POSIX shell features: 35 | # * functions; 36 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 37 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 38 | # * compound commands having a testable exit status, especially «case»; 39 | # * various built-in commands including «command», «set», and «ulimit». 40 | # 41 | # Important for patching: 42 | # 43 | # (2) This script targets any POSIX shell, so it avoids extensions provided 44 | # by Bash, Ksh, etc; in particular arrays are avoided. 45 | # 46 | # The "traditional" practice of packing multiple parameters into a 47 | # space-separated string is a well documented source of bugs and security 48 | # problems, so this is (mostly) avoided, by progressively accumulating 49 | # options in "$@", and eventually passing that to Java. 50 | # 51 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 52 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 53 | # see the in-line comments for details. 54 | # 55 | # There are tweaks for specific operating systems such as AIX, CygWin, 56 | # Darwin, MinGW, and NonStop. 57 | # 58 | # (3) This script is generated from the Groovy template 59 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 60 | # within the Gradle project. 61 | # 62 | # You can find Gradle at https://github.com/gradle/gradle/. 63 | # 64 | ############################################################################## 65 | 66 | # Attempt to set APP_HOME 67 | 68 | # Resolve links: $0 may be a link 69 | app_path=$0 70 | 71 | # Need this for daisy-chained symlinks. 72 | while 73 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 74 | [ -h "$app_path" ] 75 | do 76 | ls=$( ls -ld "$app_path" ) 77 | link=${ls#*' -> '} 78 | case $link in #( 79 | /*) app_path=$link ;; #( 80 | *) app_path=$APP_HOME$link ;; 81 | esac 82 | done 83 | 84 | # This is normally unused 85 | # shellcheck disable=SC2034 86 | APP_BASE_NAME=${0##*/} 87 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 88 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 89 | 90 | # Use the maximum available, or set MAX_FD != -1 to use that value. 91 | MAX_FD=maximum 92 | 93 | warn () { 94 | echo "$*" 95 | } >&2 96 | 97 | die () { 98 | echo 99 | echo "$*" 100 | echo 101 | exit 1 102 | } >&2 103 | 104 | # OS specific support (must be 'true' or 'false'). 105 | cygwin=false 106 | msys=false 107 | darwin=false 108 | nonstop=false 109 | case "$( uname )" in #( 110 | CYGWIN* ) cygwin=true ;; #( 111 | Darwin* ) darwin=true ;; #( 112 | MSYS* | MINGW* ) msys=true ;; #( 113 | NONSTOP* ) nonstop=true ;; 114 | esac 115 | 116 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 117 | 118 | 119 | # Determine the Java command to use to start the JVM. 120 | if [ -n "$JAVA_HOME" ] ; then 121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 122 | # IBM's JDK on AIX uses strange locations for the executables 123 | JAVACMD=$JAVA_HOME/jre/sh/java 124 | else 125 | JAVACMD=$JAVA_HOME/bin/java 126 | fi 127 | if [ ! -x "$JAVACMD" ] ; then 128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 129 | 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | if ! command -v java >/dev/null 2>&1 136 | then 137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 138 | 139 | Please set the JAVA_HOME variable in your environment to match the 140 | location of your Java installation." 141 | fi 142 | fi 143 | 144 | # Increase the maximum file descriptors if we can. 145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 146 | case $MAX_FD in #( 147 | max*) 148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 149 | # shellcheck disable=SC2039,SC3045 150 | MAX_FD=$( ulimit -H -n ) || 151 | warn "Could not query maximum file descriptor limit" 152 | esac 153 | case $MAX_FD in #( 154 | '' | soft) :;; #( 155 | *) 156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 157 | # shellcheck disable=SC2039,SC3045 158 | ulimit -n "$MAX_FD" || 159 | warn "Could not set maximum file descriptor limit to $MAX_FD" 160 | esac 161 | fi 162 | 163 | # Collect all arguments for the java command, stacking in reverse order: 164 | # * args from the command line 165 | # * the main class name 166 | # * -classpath 167 | # * -D...appname settings 168 | # * --module-path (only if needed) 169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 170 | 171 | # For Cygwin or MSYS, switch paths to Windows format before running java 172 | if "$cygwin" || "$msys" ; then 173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 174 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 175 | 176 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 177 | 178 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 179 | for arg do 180 | if 181 | case $arg in #( 182 | -*) false ;; # don't mess with options #( 183 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 184 | [ -e "$t" ] ;; #( 185 | *) false ;; 186 | esac 187 | then 188 | arg=$( cygpath --path --ignore --mixed "$arg" ) 189 | fi 190 | # Roll the args list around exactly as many times as the number of 191 | # args, so each arg winds up back in the position where it started, but 192 | # possibly modified. 193 | # 194 | # NB: a `for` loop captures its iteration list before it begins, so 195 | # changing the positional parameters here affects neither the number of 196 | # iterations, nor the values presented in `arg`. 197 | shift # remove old arg 198 | set -- "$@" "$arg" # push replacement arg 199 | done 200 | fi 201 | 202 | 203 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 204 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 205 | 206 | # Collect all arguments for the java command: 207 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 208 | # and any embedded shellness will be escaped. 209 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 210 | # treated as '${Hostname}' itself on the command line. 211 | 212 | set -- \ 213 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 214 | -classpath "$CLASSPATH" \ 215 | org.gradle.wrapper.GradleWrapperMain \ 216 | "$@" 217 | 218 | # Stop when "xargs" is not available. 219 | if ! command -v xargs >/dev/null 2>&1 220 | then 221 | die "xargs is not available" 222 | fi 223 | 224 | # Use "xargs" to parse quoted args. 225 | # 226 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 227 | # 228 | # In Bash we could simply go: 229 | # 230 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 231 | # set -- "${ARGS[@]}" "$@" 232 | # 233 | # but POSIX shell has neither arrays nor command substitution, so instead we 234 | # post-process each arg (as a line of input to sed) to backslash-escape any 235 | # character that might be a shell metacharacter, then use eval to reverse 236 | # that process (while maintaining the separation between arguments), and wrap 237 | # the whole thing up as a single "set" statement. 238 | # 239 | # This will of course break if any of these variables contains a newline or 240 | # an unmatched quote. 241 | # 242 | 243 | eval "set -- $( 244 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 245 | xargs -n1 | 246 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 247 | tr '\n' ' ' 248 | )" '"$@"' 249 | 250 | exec "$JAVACMD" "$@" 251 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | 2 | @rem 3 | @rem Copyright 2015 the original author or authors. 4 | @rem 5 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 6 | @rem you may not use this file except in compliance with the License. 7 | @rem You may obtain a copy of the License at 8 | @rem 9 | @rem https://www.apache.org/licenses/LICENSE-2.0 10 | @rem 11 | @rem Unless required by applicable law or agreed to in writing, software 12 | @rem distributed under the License is distributed on an "AS IS" BASIS, 13 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | @rem See the License for the specific language governing permissions and 15 | @rem limitations under the License. 16 | @rem 17 | 18 | @if "%DEBUG%"=="" @echo off 19 | @rem ########################################################################## 20 | @rem 21 | @rem Gradle startup script for Windows 22 | @rem 23 | @rem ########################################################################## 24 | 25 | @rem Set local scope for the variables with windows NT shell 26 | if "%OS%"=="Windows_NT" setlocal 27 | 28 | set DIRNAME=%~dp0 29 | if "%DIRNAME%"=="" set DIRNAME=. 30 | @rem This is normally unused 31 | set APP_BASE_NAME=%~n0 32 | set APP_HOME=%DIRNAME% 33 | 34 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 35 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 36 | 37 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 38 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 39 | 40 | @rem Find java.exe 41 | if defined JAVA_HOME goto findJavaFromJavaHome 42 | 43 | set JAVA_EXE=java.exe 44 | %JAVA_EXE% -version >NUL 2>&1 45 | if %ERRORLEVEL% equ 0 goto execute 46 | 47 | echo. 1>&2 48 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 49 | echo. 1>&2 50 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 51 | echo location of your Java installation. 1>&2 52 | 53 | goto fail 54 | 55 | :findJavaFromJavaHome 56 | set JAVA_HOME=%JAVA_HOME:"=% 57 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 58 | 59 | if exist "%JAVA_EXE%" goto execute 60 | 61 | echo. 1>&2 62 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 63 | echo. 1>&2 64 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 65 | echo location of your Java installation. 1>&2 66 | 67 | goto fail 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /iosApp/iosApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A93A953B29CC810C00F8E227 /* iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93A953A29CC810C00F8E227 /* iosApp.swift */; }; 11 | A93A953F29CC810D00F8E227 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A93A953E29CC810D00F8E227 /* Assets.xcassets */; }; 12 | A93A954229CC810D00F8E227 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A93A954129CC810D00F8E227 /* Preview Assets.xcassets */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | A93A953729CC810C00F8E227 /* ShakeDetection.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShakeDetection.app; sourceTree = BUILT_PRODUCTS_DIR; }; 17 | A93A953A29CC810C00F8E227 /* iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosApp.swift; sourceTree = ""; }; 18 | A93A953E29CC810D00F8E227 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 19 | A93A954129CC810D00F8E227 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 20 | /* End PBXFileReference section */ 21 | 22 | /* Begin PBXFrameworksBuildPhase section */ 23 | A93A953429CC810C00F8E227 /* Frameworks */ = { 24 | isa = PBXFrameworksBuildPhase; 25 | buildActionMask = 2147483647; 26 | files = ( 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXFrameworksBuildPhase section */ 31 | 32 | /* Begin PBXGroup section */ 33 | A93A952E29CC810C00F8E227 = { 34 | isa = PBXGroup; 35 | children = ( 36 | A93A953929CC810C00F8E227 /* iosApp */, 37 | A93A953829CC810C00F8E227 /* Products */, 38 | C4127409AE3703430489E7BC /* Frameworks */, 39 | ); 40 | sourceTree = ""; 41 | }; 42 | A93A953829CC810C00F8E227 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | A93A953729CC810C00F8E227 /* ShakeDetection.app */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | A93A953929CC810C00F8E227 /* iosApp */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | A93A953A29CC810C00F8E227 /* iosApp.swift */, 54 | A93A953E29CC810D00F8E227 /* Assets.xcassets */, 55 | A93A954029CC810D00F8E227 /* Preview Content */, 56 | ); 57 | path = iosApp; 58 | sourceTree = ""; 59 | }; 60 | A93A954029CC810D00F8E227 /* Preview Content */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | A93A954129CC810D00F8E227 /* Preview Assets.xcassets */, 64 | ); 65 | path = "Preview Content"; 66 | sourceTree = ""; 67 | }; 68 | C4127409AE3703430489E7BC /* Frameworks */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | ); 72 | name = Frameworks; 73 | sourceTree = ""; 74 | }; 75 | /* End PBXGroup section */ 76 | 77 | /* Begin PBXNativeTarget section */ 78 | A93A953629CC810C00F8E227 /* iosApp */ = { 79 | isa = PBXNativeTarget; 80 | buildConfigurationList = A93A954529CC810D00F8E227 /* Build configuration list for PBXNativeTarget "iosApp" */; 81 | buildPhases = ( 82 | A9D80A052AAB5CDE006C8738 /* ShellScript */, 83 | A93A953329CC810C00F8E227 /* Sources */, 84 | A93A953429CC810C00F8E227 /* Frameworks */, 85 | A93A953529CC810C00F8E227 /* Resources */, 86 | ); 87 | buildRules = ( 88 | ); 89 | dependencies = ( 90 | ); 91 | name = iosApp; 92 | productName = iosApp; 93 | productReference = A93A953729CC810C00F8E227 /* ShakeDetection.app */; 94 | productType = "com.apple.product-type.application"; 95 | }; 96 | /* End PBXNativeTarget section */ 97 | 98 | /* Begin PBXProject section */ 99 | A93A952F29CC810C00F8E227 /* Project object */ = { 100 | isa = PBXProject; 101 | attributes = { 102 | LastSwiftUpdateCheck = 1420; 103 | LastUpgradeCheck = 1420; 104 | TargetAttributes = { 105 | A93A953629CC810C00F8E227 = { 106 | CreatedOnToolsVersion = 14.2; 107 | }; 108 | }; 109 | }; 110 | buildConfigurationList = A93A953229CC810C00F8E227 /* Build configuration list for PBXProject "iosApp" */; 111 | compatibilityVersion = "Xcode 14.0"; 112 | developmentRegion = en; 113 | hasScannedForEncodings = 0; 114 | knownRegions = ( 115 | en, 116 | Base, 117 | ); 118 | mainGroup = A93A952E29CC810C00F8E227; 119 | productRefGroup = A93A953829CC810C00F8E227 /* Products */; 120 | projectDirPath = ""; 121 | projectRoot = ""; 122 | targets = ( 123 | A93A953629CC810C00F8E227 /* iosApp */, 124 | ); 125 | }; 126 | /* End PBXProject section */ 127 | 128 | /* Begin PBXResourcesBuildPhase section */ 129 | A93A953529CC810C00F8E227 /* Resources */ = { 130 | isa = PBXResourcesBuildPhase; 131 | buildActionMask = 2147483647; 132 | files = ( 133 | A93A954229CC810D00F8E227 /* Preview Assets.xcassets in Resources */, 134 | A93A953F29CC810D00F8E227 /* Assets.xcassets in Resources */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXResourcesBuildPhase section */ 139 | 140 | /* Begin PBXShellScriptBuildPhase section */ 141 | A9D80A052AAB5CDE006C8738 /* ShellScript */ = { 142 | isa = PBXShellScriptBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | ); 146 | inputFileListPaths = ( 147 | ); 148 | inputPaths = ( 149 | ); 150 | outputFileListPaths = ( 151 | ); 152 | outputPaths = ( 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | shellPath = /bin/sh; 156 | shellScript = "cd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; 157 | }; 158 | /* End PBXShellScriptBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | A93A953329CC810C00F8E227 /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | A93A953B29CC810C00F8E227 /* iosApp.swift in Sources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXSourcesBuildPhase section */ 170 | 171 | /* Begin XCBuildConfiguration section */ 172 | A93A954329CC810D00F8E227 /* Debug */ = { 173 | isa = XCBuildConfiguration; 174 | buildSettings = { 175 | ALWAYS_SEARCH_USER_PATHS = NO; 176 | CLANG_ANALYZER_NONNULL = YES; 177 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 178 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_ENABLE_OBJC_WEAK = YES; 182 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 183 | CLANG_WARN_BOOL_CONVERSION = YES; 184 | CLANG_WARN_COMMA = YES; 185 | CLANG_WARN_CONSTANT_CONVERSION = YES; 186 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 189 | CLANG_WARN_EMPTY_BODY = YES; 190 | CLANG_WARN_ENUM_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 194 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 195 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 198 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 199 | CLANG_WARN_STRICT_PROTOTYPES = YES; 200 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 201 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 202 | CLANG_WARN_UNREACHABLE_CODE = YES; 203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 204 | COPY_PHASE_STRIP = NO; 205 | DEBUG_INFORMATION_FORMAT = dwarf; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | ENABLE_TESTABILITY = YES; 208 | GCC_C_LANGUAGE_STANDARD = gnu11; 209 | GCC_DYNAMIC_NO_PIC = NO; 210 | GCC_NO_COMMON_BLOCKS = YES; 211 | GCC_OPTIMIZATION_LEVEL = 0; 212 | GCC_PREPROCESSOR_DEFINITIONS = ( 213 | "DEBUG=1", 214 | "$(inherited)", 215 | ); 216 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 217 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 218 | GCC_WARN_UNDECLARED_SELECTOR = YES; 219 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 220 | GCC_WARN_UNUSED_FUNCTION = YES; 221 | GCC_WARN_UNUSED_VARIABLE = YES; 222 | IPHONEOS_DEPLOYMENT_TARGET = 16.2; 223 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 224 | MTL_FAST_MATH = YES; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SDKROOT = iphoneos; 227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 229 | }; 230 | name = Debug; 231 | }; 232 | A93A954429CC810D00F8E227 /* Release */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | ALWAYS_SEARCH_USER_PATHS = NO; 236 | CLANG_ANALYZER_NONNULL = YES; 237 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 239 | CLANG_ENABLE_MODULES = YES; 240 | CLANG_ENABLE_OBJC_ARC = YES; 241 | CLANG_ENABLE_OBJC_WEAK = YES; 242 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 243 | CLANG_WARN_BOOL_CONVERSION = YES; 244 | CLANG_WARN_COMMA = YES; 245 | CLANG_WARN_CONSTANT_CONVERSION = YES; 246 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 247 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 248 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 258 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 259 | CLANG_WARN_STRICT_PROTOTYPES = YES; 260 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 261 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 262 | CLANG_WARN_UNREACHABLE_CODE = YES; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | COPY_PHASE_STRIP = NO; 265 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 266 | ENABLE_NS_ASSERTIONS = NO; 267 | ENABLE_STRICT_OBJC_MSGSEND = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu11; 269 | GCC_NO_COMMON_BLOCKS = YES; 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 16.2; 277 | MTL_ENABLE_DEBUG_INFO = NO; 278 | MTL_FAST_MATH = YES; 279 | SDKROOT = iphoneos; 280 | SWIFT_COMPILATION_MODE = wholemodule; 281 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 282 | VALIDATE_PRODUCT = YES; 283 | }; 284 | name = Release; 285 | }; 286 | A93A954629CC810D00F8E227 /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 291 | CODE_SIGN_STYLE = Automatic; 292 | CURRENT_PROJECT_VERSION = 1; 293 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; 294 | DEVELOPMENT_TEAM = ""; 295 | ENABLE_PREVIEWS = YES; 296 | GENERATE_INFOPLIST_FILE = YES; 297 | INFOPLIST_KEY_NSMotionUsageDescription = "This app uses motion data to detect shakes."; 298 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 299 | LD_RUNPATH_SEARCH_PATHS = ( 300 | "$(inherited)", 301 | "@executable_path/Frameworks", 302 | ); 303 | MARKETING_VERSION = 1.0; 304 | PRODUCT_BUNDLE_IDENTIFIER = org.chaintech.app.iosApp; 305 | PRODUCT_NAME = ShakeDetection; 306 | SWIFT_EMIT_LOC_STRINGS = YES; 307 | SWIFT_VERSION = 5.0; 308 | TARGETED_DEVICE_FAMILY = "1,2"; 309 | }; 310 | name = Debug; 311 | }; 312 | A93A954729CC810D00F8E227 /* Release */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 316 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 317 | CODE_SIGN_STYLE = Automatic; 318 | CURRENT_PROJECT_VERSION = 1; 319 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; 320 | DEVELOPMENT_TEAM = ""; 321 | ENABLE_PREVIEWS = YES; 322 | GENERATE_INFOPLIST_FILE = YES; 323 | INFOPLIST_KEY_NSMotionUsageDescription = "This app uses motion data to detect shakes."; 324 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 325 | LD_RUNPATH_SEARCH_PATHS = ( 326 | "$(inherited)", 327 | "@executable_path/Frameworks", 328 | ); 329 | MARKETING_VERSION = 1.0; 330 | PRODUCT_BUNDLE_IDENTIFIER = org.chaintech.app.iosApp; 331 | PRODUCT_NAME = ShakeDetection; 332 | SWIFT_EMIT_LOC_STRINGS = YES; 333 | SWIFT_VERSION = 5.0; 334 | TARGETED_DEVICE_FAMILY = "1,2"; 335 | }; 336 | name = Release; 337 | }; 338 | /* End XCBuildConfiguration section */ 339 | 340 | /* Begin XCConfigurationList section */ 341 | A93A953229CC810C00F8E227 /* Build configuration list for PBXProject "iosApp" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | A93A954329CC810D00F8E227 /* Debug */, 345 | A93A954429CC810D00F8E227 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | A93A954529CC810D00F8E227 /* Build configuration list for PBXNativeTarget "iosApp" */ = { 351 | isa = XCConfigurationList; 352 | buildConfigurations = ( 353 | A93A954629CC810D00F8E227 /* Debug */, 354 | A93A954729CC810D00F8E227 /* Release */, 355 | ); 356 | defaultConfigurationIsVisible = 0; 357 | defaultConfigurationName = Release; 358 | }; 359 | /* End XCConfigurationList section */ 360 | }; 361 | rootObject = A93A952F29CC810C00F8E227 /* Project object */; 362 | } 363 | -------------------------------------------------------------------------------- /iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /iosApp/iosApp/iosApp.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import ComposeApp 3 | 4 | @main 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | var window: UIWindow? 7 | 8 | func application( 9 | _ application: UIApplication, 10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 | ) -> Bool { 12 | window = UIWindow(frame: UIScreen.main.bounds) 13 | if let window = window { 14 | window.rootViewController = MainKt.MainViewController() 15 | window.makeKeyAndVisible() 16 | } 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ShakeDetection" 2 | include(":composeApp") 3 | 4 | pluginManagement { 5 | repositories { 6 | google() 7 | gradlePluginPortal() 8 | mavenCentral() 9 | } 10 | } 11 | 12 | dependencyResolutionManagement { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental") 17 | maven("https://maven.pkg.jetbrains.space/public/p/ktor/eap") 18 | } 19 | } 20 | --------------------------------------------------------------------------------