From e95eb02c4e278f99a684125440001480b15b9d36 Mon Sep 17 00:00:00 2001 From: Jemo Date: Sat, 12 Mar 2022 13:46:15 +0400 Subject: [PATCH 1/2] added Compose, currently for rally mode & Koin for DI --- androidApp/build.gradle.kts | 71 +++++--- androidApp/src/main/AndroidManifest.xml | 1 + .../dev/waroffingerskmm/android/Extensions.kt | 3 +- .../android/SelectGameActivity.kt | 41 +++-- .../android/app/MyApplication.kt | 28 ++++ .../android/app/di/UtilsModule.kt | 16 ++ .../android/app/di/ViewModels.kt | 17 ++ .../android/app/utils/DeviceIdRetriever.kt | 9 ++ .../app/utils/DeviceIdRetrieverImpl.kt | 14 ++ .../android/screens/AppRootScreen.kt | 60 +++++++ .../android/screens/AppScreens.kt | 26 +++ .../android/screens/GameMode.kt | 11 ++ .../screens/crashout/CrashOutModeScreen.kt | 21 +++ .../screens/gamemode/GameModeScreen.kt | 48 ++++++ .../android/screens/rally/RallyModeScreen.kt | 106 ++++++++++++ .../android/screens/rally/RallyViewModel.kt | 47 ++++++ .../selectplayer/SelectPlayerScreen.kt | 101 ++++++++++++ .../selectplayer/SelectPlayerViewModel.kt | 98 ++++++++++++ .../main/res/layout/activity_select_game.xml | 41 ----- .../xcschemes/xcschememanagement.plist | 10 -- shared/build.gradle.kts | 151 ++++++++++-------- .../ge/dev/waroffingerskmm/di/KoinInit.kt | 17 ++ .../dev/waroffingerskmm/di/NetworkModule.kt | 54 +++++++ .../dev/waroffingerskmm/network/KtorLogger.kt | 19 +++ .../waroffingerskmm/network/NetworkConfig.kt | 12 ++ .../socket/SocketEventsProcessor.kt | 2 +- 26 files changed, 871 insertions(+), 153 deletions(-) create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/MyApplication.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/UtilsModule.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/ViewModels.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetriever.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetrieverImpl.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppRootScreen.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppScreens.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/GameMode.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/crashout/CrashOutModeScreen.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/gamemode/GameModeScreen.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyModeScreen.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyViewModel.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerScreen.kt create mode 100644 androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerViewModel.kt delete mode 100644 androidApp/src/main/res/layout/activity_select_game.xml create mode 100644 shared/src/commonMain/kotlin/ge/dev/waroffingerskmm/di/KoinInit.kt create mode 100644 shared/src/commonMain/kotlin/ge/dev/waroffingerskmm/di/NetworkModule.kt create mode 100644 shared/src/commonMain/kotlin/ge/dev/waroffingerskmm/network/KtorLogger.kt create mode 100644 shared/src/commonMain/kotlin/ge/dev/waroffingerskmm/network/NetworkConfig.kt diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 45b934b..c55ce85 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -1,29 +1,60 @@ plugins { - id("com.android.application") - kotlin("android") + id("com.android.application") + kotlin("android") } android { - compileSdk = 31 - defaultConfig { - applicationId = "ge.dev.waroffingerskmm.android" - minSdk = 23 - targetSdk = 30 - versionCode = 1 - versionName = "1.0" - } - buildTypes { - getByName("release") { - isMinifyEnabled = false - } + compileSdk = 31 + defaultConfig { + applicationId = "ge.dev.waroffingerskmm.android" + minSdk = 23 + targetSdk = 30 + versionCode = 1 + versionName = "1.0" + } + buildTypes { + getByName("release") { + isMinifyEnabled = false } + } + buildFeatures { + // Enables Jetpack Compose for this module + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.1.1" + } } dependencies { - implementation(project(":shared")) - implementation("androidx.core:core-ktx:1.7.0") - implementation("com.google.android.material:material:1.4.0") - implementation("androidx.appcompat:appcompat:1.3.1") - implementation("androidx.constraintlayout:constraintlayout:2.1.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") + implementation(project(":shared")) + implementation("androidx.core:core-ktx:1.7.0") + implementation("com.google.android.material:material:1.5.0") + implementation("androidx.appcompat:appcompat:1.4.1") + implementation("androidx.constraintlayout:constraintlayout:2.1.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") + + implementation("io.insert-koin:koin-android:3.2.0-beta-1") + implementation("io.insert-koin:koin-androidx-compose:3.2.0-beta-1") + + + // Integration with activities + implementation("androidx.activity:activity-compose:1.4.0") + // Navigation + implementation("androidx.navigation:navigation-compose:2.4.1") + implementation("androidx.compose.ui:ui:1.1.1") + // Tooling support (Previews, etc.) + implementation("androidx.compose.ui:ui-tooling:1.1.1") + // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.) + implementation("androidx.compose.foundation:foundation:1.1.1") + // Material Design + implementation("androidx.compose.material:material:1.1.1") + // ConstraintLayout + implementation("androidx.constraintlayout:constraintlayout-compose:1.0.0") + + // Material design icons + implementation("androidx.compose.material:material-icons-core:1.1.1") + implementation("androidx.compose.material:material-icons-extended:1.1.1") + // Integration with observables + implementation("androidx.compose.runtime:runtime-livedata:1.1.1") } \ No newline at end of file diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index 82d2ce5..226e0dd 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ (R.id.btnCrash).setOnClickListener { - val intent = Intent(this, SelectCarCrashPlayerActivity::class.java) - startActivity(intent) + setContent { + MainApp() } + fullscreen() + } - findViewById(R.id.btnRally).setOnClickListener { - val intent = Intent(this, SelectPlayerActivity::class.java) - startActivity(intent) - } + @Preview + @Composable + fun Preview() { + MainApp() } } \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/MyApplication.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/MyApplication.kt new file mode 100644 index 0000000..3b914be --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/MyApplication.kt @@ -0,0 +1,28 @@ +package ge.dev.waroffingerskmm.android.app + +import android.app.Application +import ge.dev.waroffingerskmm.android.app.di.UtilsModule +import ge.dev.waroffingerskmm.android.app.di.ViewModels +import ge.dev.waroffingerskmm.di.initKoin +import ge.dev.waroffingerskmm.network.NetworkConfig +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.logger.Level + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +class MyApplication : Application() { + override fun onCreate() { + super.onCreate() + initKoin(appDeclaration = { + androidLogger(Level.INFO) + androidContext(this@MyApplication) + modules( + ViewModels.module, + UtilsModule.module + ) + }, NetworkConfig(baseUrl = "")) + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/UtilsModule.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/UtilsModule.kt new file mode 100644 index 0000000..f3db68b --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/UtilsModule.kt @@ -0,0 +1,16 @@ +package ge.dev.waroffingerskmm.android.app.di + +import ge.dev.waroffingerskmm.android.app.utils.DeviceIdRetriever +import ge.dev.waroffingerskmm.android.app.utils.DeviceIdRetrieverImpl +import org.koin.android.ext.koin.androidContext +import org.koin.dsl.module + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +object UtilsModule { + val module = module { + single { DeviceIdRetrieverImpl(context = androidContext()) as DeviceIdRetriever} + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/ViewModels.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/ViewModels.kt new file mode 100644 index 0000000..24c83a1 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/di/ViewModels.kt @@ -0,0 +1,17 @@ +package ge.dev.waroffingerskmm.android.app.di + +import ge.dev.waroffingerskmm.android.screens.rally.RallyViewModel +import ge.dev.waroffingerskmm.android.screens.selectplayer.SelectPlayerViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +object ViewModels { + val module = module { + viewModel { SelectPlayerViewModel(socketEventsProcessor = get(), deviceIdRetriever = get()) } + viewModel { RallyViewModel(socketEventsProcessor = get()) } + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetriever.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetriever.kt new file mode 100644 index 0000000..0751dd1 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetriever.kt @@ -0,0 +1,9 @@ +package ge.dev.waroffingerskmm.android.app.utils + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +interface DeviceIdRetriever { + fun getDeviceId(): String +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetrieverImpl.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetrieverImpl.kt new file mode 100644 index 0000000..28e27a6 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/app/utils/DeviceIdRetrieverImpl.kt @@ -0,0 +1,14 @@ +package ge.dev.waroffingerskmm.android.app.utils + +import android.content.Context +import ge.dev.waroffingerskmm.android.getDeviceId + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +class DeviceIdRetrieverImpl(private val context: Context) : DeviceIdRetriever{ + override fun getDeviceId(): String { + return context.getDeviceId() + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppRootScreen.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppRootScreen.kt new file mode 100644 index 0000000..808eedd --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppRootScreen.kt @@ -0,0 +1,60 @@ +package ge.dev.waroffingerskmm.android.screens + +import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import ge.dev.waroffingerskmm.android.screens.crashout.CrashOutModeScreen +import ge.dev.waroffingerskmm.android.screens.gamemode.GameModeScreen +import ge.dev.waroffingerskmm.android.screens.rally.RallyModeScreen +import ge.dev.waroffingerskmm.android.screens.selectplayer.SelectPlayerScreen + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ + +@Composable +fun MainApp() { + val navController = rememberNavController() + NavigationComponent(navController = navController) +} + +@Composable +fun NavigationComponent(navController: NavHostController) { + NavHost( + navController = navController, + startDestination = AppScreens.SelectGameMode.name + ) { + composable(AppScreens.SelectGameMode.name) { + GameModeScreen(navController) + } + composable( + route = AppScreens.SelectPlayer.name.plus("/{${RouteKeys.GAME_MODE}}"), + arguments = listOf(navArgument(RouteKeys.GAME_MODE) { type = NavType.StringType }) + ) { + val gameModeStr: String = it.arguments?.getString(RouteKeys.GAME_MODE) ?: return@composable + val gameMode = GameMode.valueOf(gameModeStr) + SelectPlayerScreen(navController, gameMode) + } + composable( + route = AppScreens.RallyScreen.name.plus("/{${RouteKeys.SELECTED_PLAYER_ID}}"), + arguments = listOf(navArgument(RouteKeys.SELECTED_PLAYER_ID) { type = NavType.StringType }) + ) { + val playerId = it.arguments?.getString(RouteKeys.SELECTED_PLAYER_ID) ?: return@composable + RallyModeScreen(playerId) + } + composable(AppScreens.CrashOutScreen.name) { + CrashOutModeScreen() + } + } +} + +object RouteKeys { + const val GAME_MODE = "GAME_MODE" + const val SELECTED_PLAYER_ID = "SELECTED_PLAYER_ID" +} + diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppScreens.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppScreens.kt new file mode 100644 index 0000000..898e360 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/AppScreens.kt @@ -0,0 +1,26 @@ +package ge.dev.waroffingerskmm.android.screens + +import androidx.compose.runtime.Composable +import ge.dev.waroffingerskmm.android.screens.gamemode.GameModeScreen + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +enum class AppScreens() { + SelectGameMode, + SelectPlayer, + RallyScreen, + CrashOutScreen; + + + companion object { + fun fromRoute(route: String?): AppScreens = + when (route?.substringBefore("/")) { + SelectGameMode.name -> SelectGameMode + SelectPlayer.name -> SelectPlayer + null -> SelectGameMode + else -> throw IllegalArgumentException("Route $route is not recognized.") + } + } +} diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/GameMode.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/GameMode.kt new file mode 100644 index 0000000..b22c941 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/GameMode.kt @@ -0,0 +1,11 @@ +package ge.dev.waroffingerskmm.android.screens + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +enum class GameMode { + Rally, + CrashOut, + None +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/crashout/CrashOutModeScreen.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/crashout/CrashOutModeScreen.kt new file mode 100644 index 0000000..28e0650 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/crashout/CrashOutModeScreen.kt @@ -0,0 +1,21 @@ +package ge.dev.waroffingerskmm.android.screens.crashout + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ + +@Composable +fun CrashOutModeScreen() { + Box(modifier = Modifier + .fillMaxHeight() + .fillMaxWidth()){ + + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/gamemode/GameModeScreen.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/gamemode/GameModeScreen.kt new file mode 100644 index 0000000..25ca74c --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/gamemode/GameModeScreen.kt @@ -0,0 +1,48 @@ +package ge.dev.waroffingerskmm.android.screens.gamemode + +import androidx.compose.foundation.layout.* +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import androidx.navigation.NavOptions +import androidx.navigation.NavOptionsBuilder +import ge.dev.waroffingerskmm.android.screens.AppScreens +import ge.dev.waroffingerskmm.android.screens.GameMode + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ + +@Composable +fun GameModeScreen(navController: NavController) { + Box( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("Select Game") + ButtonComponent("Rally") { + navController.navigate(AppScreens.SelectPlayer.name.plus("/${GameMode.Rally.name}")) + } + Spacer(modifier = Modifier.width(8.dp)) + ButtonComponent("Crash Out") { + navController.navigate(AppScreens.SelectPlayer.name.plus("/${GameMode.CrashOut.name}")) + } + } + } +} + +@Composable +fun ButtonComponent(title: String, onClick: (() -> Unit)) { + Button(onClick = onClick, modifier = Modifier.width(150.dp)) { + Text(title) + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyModeScreen.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyModeScreen.kt new file mode 100644 index 0000000..76a6220 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyModeScreen.kt @@ -0,0 +1,106 @@ +package ge.dev.waroffingerskmm.android.screens.rally + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintLayoutBaseScope +import androidx.constraintlayout.compose.ConstraintLayoutScope +import ge.dev.waroffingerskmm.android.R +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import org.koin.androidx.compose.getViewModel +import androidx.compose.runtime.getValue + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ + +@Composable +fun RallyModeScreen(selectedPlayerId: String, viewModel: RallyViewModel = getViewModel()) { + ConstraintLayout( + modifier = Modifier + .fillMaxSize() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { viewModel.onTap() } + ) { + viewModel.selectedCarId = selectedPlayerId + + val offset1 by animateDpAsState(viewModel.offsetCar1.dp) + val offset2 by animateDpAsState(viewModel.offsetCar2.dp) + + val (car1, road1, car2, road2) = createRefs() + + Image( + painter = painterResource(id = R.drawable.ic_road), + contentScale = ContentScale.FillBounds, + contentDescription = "road 1", + modifier = Modifier + .constrainAs(road1) { + bottom.linkTo(parent.bottom) + start.linkTo(parent.start) + } + .fillMaxHeight() + .width(150.dp) + ) + + Image( + painter = painterResource(id = R.drawable.red_car), + contentDescription = "red car", + modifier = Modifier + .constrainAs(car1) { + bottom.linkTo(parent.bottom, margin = 20.dp) + start.linkTo(road1.start) + end.linkTo(road1.end) + } + .offset(y = offset1) + .width(100.dp) + .height(100.dp) + ) + + Image( + painter = painterResource(id = R.drawable.ic_road), + contentScale = ContentScale.FillBounds, + contentDescription = "road 2", + modifier = Modifier + .constrainAs(road2) { + bottom.linkTo(parent.bottom) + end.linkTo(parent.end) + } + .fillMaxHeight() + .width(150.dp) + ) + + Image( + painter = painterResource(id = R.drawable.car_yellow), + contentDescription = "red car", + modifier = Modifier + .constrainAs(car2) { + bottom.linkTo(parent.bottom, margin = 20.dp) + start.linkTo(road2.start) + end.linkTo(road2.end) + } + .offset(y = offset2) + .width(100.dp) + .height(100.dp) + ) + } +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyViewModel.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyViewModel.kt new file mode 100644 index 0000000..9f8b4d8 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/rally/RallyViewModel.kt @@ -0,0 +1,47 @@ +package ge.dev.waroffingerskmm.android.screens.rally + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.unit.dp +import androidx.lifecycle.ViewModel +import ge.dev.waroffingerskmm.model.SelectionResult +import ge.dev.waroffingerskmm.socket.Events +import ge.dev.waroffingerskmm.socket.SocketEventsProcessor +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +class RallyViewModel(private val socketEventsProcessor: SocketEventsProcessor) : ViewModel() { + var offsetCar1 by mutableStateOf(0f) + private set + + var offsetCar2 by mutableStateOf(0f) + private set + + var selectedCarId: String? = null + + init { + socketEventsProcessor.connect() + socketEventsProcessor.onTapEventReceived = { data -> + String + val result = Json.decodeFromString(data) + val isP1 = result.p1 + if (isP1) { + offsetCar1 += 5.dp.value * -1 + } else { + offsetCar2 += 5.dp.value * -1 + } + } + } + + fun onTap() { + selectedCarId?.let { + socketEventsProcessor.sendData(Events.Request.ON_TAP, it) + } + } + +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerScreen.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerScreen.kt new file mode 100644 index 0000000..efdddb4 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerScreen.kt @@ -0,0 +1,101 @@ +package ge.dev.waroffingerskmm.android.screens.selectplayer + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import ge.dev.waroffingerskmm.android.R +import ge.dev.waroffingerskmm.android.screens.AppScreens +import ge.dev.waroffingerskmm.android.screens.GameMode +import org.koin.androidx.compose.getViewModel + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ + +@Composable +fun SelectPlayerScreen(navController: NavController, gameMode: GameMode) { + val vm: SelectPlayerViewModel = getViewModel() + vm.gameMode = gameMode + val p1Color = if (vm.firstPlayerState) Color.Green else Color.Red + val p2Color = if (vm.secondPlayerState) Color.Green else Color.Red + if (vm.startGame) { + when (gameMode) { + GameMode.CrashOut -> { + navController.navigate(AppScreens.CrashOutScreen.name.plus("/${vm.getSelectedPlayerId()}")) + } + GameMode.Rally -> { + navController.navigate(AppScreens.RallyScreen.name.plus("/${vm.getSelectedPlayerId()}")) + } + else -> {} + } + vm.resetGameState() + } + + Box( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + contentAlignment = Alignment.Center + ) { + Column() { + Row() { + Text(text = "P1") + Spacer(modifier = Modifier.width(5.dp)) + PlayerStatusShape(p1Color) + CarImage(R.drawable.red_car) { + vm.firstPlayerSelected() + } + } + Spacer(modifier = Modifier.height(20.dp)) + Row() { + Text(text = "P2") + Spacer(modifier = Modifier.width(5.dp)) + PlayerStatusShape(p2Color) + CarImage(R.drawable.car_yellow) { + vm.secondPlayerSelected() + } + } + } + } + +} + + +@Composable +fun CarImage(@DrawableRes drawableRes: Int, onClick: () -> Unit) { + Image( + painter = painterResource(drawableRes), + contentDescription = "car image", + modifier = Modifier + .size(150.dp) + .clip(CircleShape) + .clickable(onClick = onClick) + .border(2.dp, MaterialTheme.colors.secondary, CircleShape) + ) +} + + +@Composable +fun PlayerStatusShape(color: Color) { + Box( + modifier = Modifier + .size(10.dp) + .clip(CircleShape) + .background(color) + ) +} \ No newline at end of file diff --git a/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerViewModel.kt b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerViewModel.kt new file mode 100644 index 0000000..0ec1463 --- /dev/null +++ b/androidApp/src/main/java/ge/dev/waroffingerskmm/android/screens/selectplayer/SelectPlayerViewModel.kt @@ -0,0 +1,98 @@ +package ge.dev.waroffingerskmm.android.screens.selectplayer + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import ge.dev.waroffingerskmm.android.PlayerConstants +import ge.dev.waroffingerskmm.android.app.utils.DeviceIdRetriever +import ge.dev.waroffingerskmm.android.screens.GameMode +import ge.dev.waroffingerskmm.model.PlayerInfo +import ge.dev.waroffingerskmm.model.SelectedPlayer +import ge.dev.waroffingerskmm.socket.Events +import ge.dev.waroffingerskmm.socket.SocketEventsProcessor +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +/** + * @Author: Jemo Mgebrishvili + * @Date: 11.03.22 + */ +class SelectPlayerViewModel( + private val socketEventsProcessor: SocketEventsProcessor, + private val deviceIdRetriever: DeviceIdRetriever +) : ViewModel() { + + private var selectedPlayerId: String? = null + + fun getSelectedPlayerId() = selectedPlayerId + + var firstPlayerState: Boolean by mutableStateOf(false) + private set + var secondPlayerState: Boolean by mutableStateOf(false) + private set + + var startGame: Boolean by mutableStateOf(false) + private set + + var gameMode = GameMode.None + + fun resetGameState() { + startGame = false + socketEventsProcessor.disconnect() + viewModelScope.launch { + delay(100) + secondPlayerState = false + firstPlayerState = false + } + } + + init { + socketEventsProcessor.connect() + socketEventsProcessor.onPlayerSelected = { + try { + println("onPlayerSelected $it") + val data = Json.decodeFromString(it) + val selectedCarId = data.id + if (selectedCarId == PlayerConstants.PLAYER_1_ID.toString()) { + firstPlayerState = true + } else { + secondPlayerState = true + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + socketEventsProcessor.onPlayersReady = { + startGame = true + } + } + + fun firstPlayerSelected() { + selectedPlayerId = PlayerConstants.PLAYER_1_ID.toString() + playerSelected() + + } + + fun secondPlayerSelected() { + selectedPlayerId = PlayerConstants.PLAYER_2_ID.toString() + playerSelected() + } + + private fun playerSelected() { + if (selectedPlayerId == null) return + val playerInfo = Json.encodeToString( + PlayerInfo( + playerId = selectedPlayerId!!, + deviceId = deviceIdRetriever.getDeviceId() + ) + ) + println("json_ $playerInfo") + socketEventsProcessor.sendData(Events.Request.PLAYER_SELECTED, playerInfo) + } +} \ No newline at end of file diff --git a/androidApp/src/main/res/layout/activity_select_game.xml b/androidApp/src/main/res/layout/activity_select_game.xml deleted file mode 100644 index a56dda9..0000000 --- a/androidApp/src/main/res/layout/activity_select_game.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - -