diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08e447d..54c116e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,9 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("androidx.navigation.safeargs") + id("kotlin-parcelize") + kotlin("kapt") + id("com.google.dagger.hilt.android") } android { @@ -55,4 +58,14 @@ dependencies { // Navigation implementation("androidx.navigation:navigation-fragment-ktx:2.7.6") implementation("androidx.navigation:navigation-ui-ktx:2.7.6") + + /** + * DI Dagger Hilt + */ + implementation("com.google.dagger:hilt-android:2.50") + kapt("com.google.dagger:hilt-android-compiler:2.50") +} + +kapt { + correctErrorTypes = true } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 39ef3f5..f91c64e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MemoryGame" + android:name=".app.App" tools:targetApi="31"> } \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/domain/impl/AppRepositoryImpl.kt b/app/src/main/java/uz/gita/memorygame/domain/impl/AppRepositoryImpl.kt index 30025e4..f2e7949 100644 --- a/app/src/main/java/uz/gita/memorygame/domain/impl/AppRepositoryImpl.kt +++ b/app/src/main/java/uz/gita/memorygame/domain/impl/AppRepositoryImpl.kt @@ -1,7 +1,78 @@ package uz.gita.memorygame.domain.impl +import uz.gita.memorygame.R +import uz.gita.memorygame.data.CardData import uz.gita.memorygame.domain.AppRepository +import javax.inject.Inject +import javax.inject.Singleton -class AppRepositoryImpl : AppRepository { +@Singleton +class AppRepositoryImpl @Inject constructor( +) : AppRepository { + private val imageIDList = listOf( + R.drawable.image_1, + R.drawable.image_2, + R.drawable.image_3, + R.drawable.image_4, + R.drawable.image_5, + R.drawable.image_6, + R.drawable.image_7, + R.drawable.image_8, + R.drawable.image_9, + R.drawable.image_10, + R.drawable.image_11, + R.drawable.image_12, + R.drawable.image_13, + R.drawable.image_14, + R.drawable.image_15, + R.drawable.image_16, + R.drawable.image_17, + R.drawable.image_18, + R.drawable.image_19, + R.drawable.image_20, + R.drawable.image_21, + R.drawable.image_22, + R.drawable.image_23, + R.drawable.image_24, + R.drawable.image_25, + R.drawable.image_26, + R.drawable.image_27, + R.drawable.image_28, + R.drawable.image_29, + R.drawable.image_30, + R.drawable.image_31, + R.drawable.image_32, + R.drawable.image_33, + R.drawable.image_34, + R.drawable.image_35, + R.drawable.image_36, + R.drawable.image_37, + R.drawable.image_38, + R.drawable.image_39, + R.drawable.image_40, + R.drawable.image_41, + R.drawable.image_42, + R.drawable.image_43, + R.drawable.image_44, + R.drawable.image_45, + R.drawable.image_46, + R.drawable.image_47, + R.drawable.image_48, + R.drawable.image_49, + R.drawable.image_50, + R.drawable.image_51, + R.drawable.image_52, + R.drawable.image_53, + R.drawable.image_54 + ) + + override fun getCardsByLevel(countX: Int, countY: Int): List { + val result = ArrayList() + + val images = imageIDList.shuffled().subList(0, countX * countY / 2) + repeat(2) {result.addAll(images)} + + return result.shuffled() + } } \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/presentation/screens/GameScreen.kt b/app/src/main/java/uz/gita/memorygame/presentation/screens/GameScreen.kt index b952508..d3505bf 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/screens/GameScreen.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/screens/GameScreen.kt @@ -4,18 +4,26 @@ import android.os.Bundle import android.view.View import android.widget.ImageView import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import by.kirich1409.viewbindingdelegate.viewBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch import uz.gita.memorygame.R -import uz.gita.memorygame.data.ImagesMapper import uz.gita.memorygame.databinding.ScreenGameBinding +import uz.gita.memorygame.presentation.viewmodels.GameViewModel +import uz.gita.memorygame.presentation.viewmodels.impl.GameViewModelImpl +import uz.gita.memorygame.utils.hideImage import uz.gita.memorygame.utils.myApply +import uz.gita.memorygame.utils.openImage +import uz.gita.memorygame.utils.removeImage +@AndroidEntryPoint class GameScreen : Fragment(R.layout.screen_game) { private val binding by viewBinding(ScreenGameBinding::bind) + private val viewModel: GameViewModel by viewModels() private val navArgs by navArgs() private var views = ArrayList() private var countX: Int = 0 @@ -24,128 +32,52 @@ class GameScreen : Fragment(R.layout.screen_game) { private var cardHeight: Float = 0f private var firstImageIndex = -1 private var secondImageIndex = -1 - private var openedImages = ArrayList() - private var imagesID = ArrayList() + private var findCardsCount = 0 override fun onViewCreated(view: View, savedInstanceState: Bundle?) = binding.myApply { super.onViewCreated(view, savedInstanceState) - binding.container.post { - countY = navArgs.verticalCardCount - countX = navArgs.horizontalCardCount + countY = navArgs.verticalCardCount + countX = navArgs.horizontalCardCount + binding.container.post { cardHeight = container.height.toFloat() / countY cardWidth = container.width.toFloat() / countX - loadImagesID() - fillListOpenedImages() - setImages() - addClickEvent() - } - } + val ls = viewModel.getCardsByLevel(countX, countY) - private fun loadImagesID() { - repeat(2) { - for (i in 0 until countY * countX / 2) { - imagesID.add(ImagesMapper.map[i]!!) + loadImages(ls) + lifecycleScope.launch { + delay(1000) // 1 soniya kartalarni ochiq ushlab turadi, + hideCards() + delay(1000) // animatsiya bilan yopilishi uchun 1 soniya ketadi + setCardsClick() } } - imagesID.shuffle() - } + reload.setOnClickListener { - private fun fillListOpenedImages() { - repeat(countX * countY) { - openedImages.add(false) } + menu.setOnClickListener { } } - private fun addClickEvent() { - views.forEachIndexed { index, image -> - image.setOnClickListener { - if (openedImages[index]) { - if (firstImageIndex != -1) { - firstImageIndex = -1 - } else if (secondImageIndex != -1) { - secondImageIndex = -1 - } else { - return@setOnClickListener - } - - image.animate() - .setDuration(500) - .rotationY(-89f) - .withEndAction { - image.rotationY = 89f - image.setImageResource(R.drawable.image_back) - image.animate() - .setDuration(1000) - .rotationY(0f) - .start() - } - .start() - openedImages[index] = false - - } else { - if (firstImageIndex == -1) { - firstImageIndex = index - } else if (secondImageIndex == -1) { - secondImageIndex = index - } else { - return@setOnClickListener - } - - image.animate() - .setDuration(500) - .rotationY(89f) - .withEndAction { - image.rotationY = -89f // 271 - image.setImageResource(imagesID[index]) - - image.animate() - .setDuration(1000) - .rotationY(0f) - .start() - } - .start() - openedImages[index] = true - } - - if (firstImageIndex != -1 && secondImageIndex != -1) { - if (imagesID[firstImageIndex] == imagesID[secondImageIndex]) { - lifecycleScope.launch { - delay(1000) - views[firstImageIndex].animate() - .setDuration(1000) - .alpha(0.1f) - .start() - views[secondImageIndex].animate() - .setDuration(1000) - .alpha(0.1f) - .start() - - firstImageIndex = -1 - secondImageIndex = -1 - } - } else { - - } - } - } + private fun hideCards() { + views.forEach { + it.hideImage() } } - private fun setImages() { + private fun loadImages(images: List) { for (i in 0 until countY) { for (j in 0 until countX) { val img = ImageView(requireContext()) - img.animate() - .setDuration(500) - .x(j * cardWidth) - .y(i * cardHeight) - .start() + img.isClickable = false + img.setImageResource(images[i * countX + j]) + + img.x = j * cardWidth + img.y = i * cardHeight + img.tag = images[i * countX + j] - img.setImageResource(R.drawable.image_back) binding.container.addView(img) img.layoutParams.apply { @@ -157,4 +89,55 @@ class GameScreen : Fragment(R.layout.screen_game) { } } } + + private fun setCardsClick() { + views.forEachIndexed { index, imageView -> + imageView.setOnClickListener { + if (firstImageIndex != -1 && secondImageIndex != -1) return@setOnClickListener + imageView.openImage() + + if (firstImageIndex == -1) { + firstImageIndex = index + } else { + secondImageIndex = index + check() + } + } + } + } + + private fun check() { + val first = views[firstImageIndex] + val second = views[secondImageIndex] + + if (first.tag == second.tag) { + lifecycleScope.launch { + delay(1000) + + first.removeImage() + second.removeImage { + firstImageIndex = -1 + secondImageIndex = -1 + } + } + } else { + lifecycleScope.launch { + delay(1000) + first.hideImage() + second.hideImage() + + delay(1000) // animatsiya tugashi uchun 1 soniya ushlab turadi + first.isClickable = true + second.isClickable = true + + firstImageIndex = -1 + secondImageIndex = -1 + findCardsCount += 2 + + if (findCardsCount == countX * countY) { + viewModel.userWin() + } + } + } + } } diff --git a/app/src/main/java/uz/gita/memorygame/presentation/screens/LevelScreen.kt b/app/src/main/java/uz/gita/memorygame/presentation/screens/LevelScreen.kt index d48e7ec..3c31f0d 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/screens/LevelScreen.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/screens/LevelScreen.kt @@ -5,9 +5,11 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import by.kirich1409.viewbindingdelegate.viewBinding +import dagger.hilt.android.AndroidEntryPoint import uz.gita.memorygame.R import uz.gita.memorygame.databinding.ScreenLevelBinding +@AndroidEntryPoint class LevelScreen : Fragment(R.layout.screen_level) { private val binding by viewBinding(ScreenLevelBinding::bind) @@ -19,10 +21,10 @@ class LevelScreen : Fragment(R.layout.screen_level) { startGame(2, 3) } medium.setOnClickListener { - startGame(6, 8) + startGame(3, 6) } hard.setOnClickListener { - startGame(8, 10) + startGame(4, 6) } } } diff --git a/app/src/main/java/uz/gita/memorygame/presentation/screens/SplashScreen.kt b/app/src/main/java/uz/gita/memorygame/presentation/screens/SplashScreen.kt index a420831..848198b 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/screens/SplashScreen.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/screens/SplashScreen.kt @@ -5,10 +5,12 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch import uz.gita.memorygame.R +@AndroidEntryPoint class SplashScreen : Fragment(R.layout.screen_splash) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/GameViewModel.kt b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/GameViewModel.kt index c6b433e..fa5f9dc 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/GameViewModel.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/GameViewModel.kt @@ -1,9 +1,8 @@ package uz.gita.memorygame.presentation.viewmodels -import kotlinx.coroutines.flow.Flow - interface GameViewModel { - val initCards : Flow - fun initCards() + fun getCardsByLevel(countX: Int, countY: Int) : List + fun userWin() + } \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/GameViewModelImpl.kt b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/GameViewModelImpl.kt index 9b82920..e240249 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/GameViewModelImpl.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/GameViewModelImpl.kt @@ -1,17 +1,21 @@ package uz.gita.memorygame.presentation.viewmodels.impl import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch +import dagger.hilt.android.lifecycle.HiltViewModel +import uz.gita.memorygame.domain.AppRepository import uz.gita.memorygame.presentation.viewmodels.GameViewModel +import javax.inject.Inject -class GameViewModelImpl : GameViewModel, ViewModel() { - override val initCards = MutableSharedFlow() +@HiltViewModel +class GameViewModelImpl +@Inject constructor( + private val repository: AppRepository +) : GameViewModel, ViewModel() { + + override fun getCardsByLevel(countX: Int, countY: Int) = + repository.getCardsByLevel(countX, countY) + + override fun userWin() { - override fun initCards() { - viewModelScope.launch { - initCards.emit(Unit) - } } } \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/LevelViewModelImpl.kt b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/LevelViewModelImpl.kt index 58a3166..1358cfc 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/LevelViewModelImpl.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/LevelViewModelImpl.kt @@ -1,11 +1,13 @@ package uz.gita.memorygame.presentation.viewmodels.impl import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel import uz.gita.memorygame.domain.AppRepository -import uz.gita.memorygame.domain.impl.AppRepositoryImpl import uz.gita.memorygame.presentation.viewmodels.LevelViewModel +import javax.inject.Inject -class LevelViewModelImpl() : ViewModel(), LevelViewModel { - private val repository : AppRepository = AppRepositoryImpl() - -} \ No newline at end of file +@HiltViewModel +class LevelViewModelImpl +@Inject constructor( + private val repository: AppRepository +) : ViewModel(), LevelViewModel \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/SplashViewModelImpl.kt b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/SplashViewModelImpl.kt index ae97913..32f71ca 100644 --- a/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/SplashViewModelImpl.kt +++ b/app/src/main/java/uz/gita/memorygame/presentation/viewmodels/impl/SplashViewModelImpl.kt @@ -1,11 +1,14 @@ package uz.gita.memorygame.presentation.viewmodels.impl import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel import uz.gita.memorygame.domain.AppRepository -import uz.gita.memorygame.domain.impl.AppRepositoryImpl import uz.gita.memorygame.presentation.viewmodels.SplashViewModel +import javax.inject.Inject -class SplashViewModelImpl: ViewModel(), SplashViewModel { - private val repository : AppRepository = AppRepositoryImpl() +@HiltViewModel +class SplashViewModelImpl @Inject constructor( + private val repository: AppRepository +) : ViewModel(), SplashViewModel { } \ No newline at end of file diff --git a/app/src/main/java/uz/gita/memorygame/utils/Utils.kt b/app/src/main/java/uz/gita/memorygame/utils/Utils.kt index e9f57a1..f7b358c 100644 --- a/app/src/main/java/uz/gita/memorygame/utils/Utils.kt +++ b/app/src/main/java/uz/gita/memorygame/utils/Utils.kt @@ -1,5 +1,6 @@ package uz.gita.memorygame.utils +import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.flowWithLifecycle @@ -11,6 +12,10 @@ fun T.myApply(block: T.() -> Unit) { block(this) } +fun logger(message: String) { + Log.d("TTT", message) +} + fun Flow.launchLifecycle( lifecycle: Lifecycle, lifecycleCoroutineScope: LifecycleCoroutineScope, diff --git a/build.gradle.kts b/build.gradle.kts index 57353bb..3950a66 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,4 +13,5 @@ buildscript { plugins { id("com.android.application") version "8.2.1" apply false id("org.jetbrains.kotlin.android") version "1.9.22" apply false + id("com.google.dagger.hilt.android") version "2.50" apply false } \ No newline at end of file