├── 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 |
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 | 
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 |
--------------------------------------------------------------------------------