diff --git a/.gitignore b/.gitignore
index 8a6b3320..4472a6c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
!.idea/codeStyles
.DS_Store
/build
+/app/build/
/captures
.externalNativeBuild
.cxx
-
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..26d33521
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..4e9ba356
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Finema
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 4d30507b..118a5c1c 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..61a9130c
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 00000000..b617266a
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 00000000..a5f05cd8
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..d5d35ec4
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/.idea/codeStyles/Project.xml b/app/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..4d30507b
--- /dev/null
+++ b/app/.idea/codeStyles/Project.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/codeStyles/codeStyleConfig.xml b/app/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/app/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/CI.md b/app/CI.md
new file mode 100644
index 00000000..f334a698
--- /dev/null
+++ b/app/CI.md
@@ -0,0 +1,20 @@
+# Continius Integration
+
+Это CI для студентческих проектов Технопарка.
+
+## Запуск локально
+
+Для запуска проверки стиля кода необходимо запустить три скрипта:
+1. Запустить `./detekt` или `.\detekt.bat`
+2. Запустить `./ktlint` или `.\ktlint.bat`
+3. Запустить `./checkstyle` или `.\checkstyle.bat`
+
+
+## Исправление ошибок
+
+В некоторых утилитах есть автоматическое исправление ошибок.
+Например, большинство ошибок можно поправить автоформаттером ktlint:
+
+```
+./ktlint -F
+```
diff --git a/app/README.md b/app/README.md
new file mode 100644
index 00000000..99e9da4e
--- /dev/null
+++ b/app/README.md
@@ -0,0 +1,11 @@
+# Movie App
+
+Приложение для выбора лучшего фильма. Предлагается какое-то кол-во фильмов из одного жанра и пользователь выбирает лучшее посредством турнира.
+
+## Команда авторов
+
+- [Иван Абрамов](https://github.com/Alberto195)
+- [Дмитрий Костык](https://github.com/kodzzzima)
+- [Иван Цыганов](https://github.com/fatalem0)
+- [Иван Демидов](https://github.com/GypsyJR777)
+
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 00000000..8398f292
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,171 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+ id 'androidx.navigation.safeargs'
+ id 'com.google.gms.google-services'
+}
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-parcelize'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.example.finema"
+ minSdkVersion 21
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildFeatures {
+ viewBinding true
+ dataBinding true
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+ext {
+ dagger_version = '2.30.1'
+ appcompat_version = '1.2.0'
+ lifecycle_version = '2.3.1'
+ activity_version = '1.3.0-alpha07'
+ fragment_version = '1.3.3'
+ nav_version = '2.3.5'
+ koin_version= "3.0.1"
+ pagingVersion = "3.0.0"
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ implementation 'com.google.firebase:firebase-auth:20.0.4'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'com.google.android.gms:play-services-ads:20.1.0'
+ implementation 'com.google.android.gms:play-services-maps:17.0.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'com.google.firebase:firebase-database:20.0.0'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+ //firebase
+ implementation 'com.google.firebase:firebase-auth-ktx'
+ implementation 'com.google.firebase:firebase-firestore-ktx'
+ implementation 'com.google.firebase:firebase-analytics-ktx'
+ implementation platform('com.google.firebase:firebase-bom:27.1.0')
+
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+ //Dagger 2
+ api "com.google.dagger:dagger:$dagger_version"
+ kapt "com.google.dagger:dagger-compiler:$dagger_version"
+ api "com.google.dagger:dagger-android-support:$dagger_version" // if you use the support libraries
+ kapt "com.google.dagger:dagger-android-processor:$dagger_version"
+
+ //Rxjava2
+ implementation "io.reactivex.rxjava2:rxjava:2.2.10"
+ implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
+
+ //Retrofit + OkHttp3
+ implementation "com.squareup.okhttp3:okhttp:4.7.2"
+ implementation "com.squareup.okhttp3:logging-interceptor:4.6.0"
+ implementation "com.squareup.retrofit2:retrofit:2.9.0"
+ implementation "com.squareup.retrofit2:converter-gson:2.9.0"
+ implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
+ implementation "com.squareup.retrofit2:adapter-rxjava2:2.5.0"
+
+ //appcompat
+ implementation "androidx.appcompat:appcompat:$appcompat_version"
+ implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
+
+ //lifecycle
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+ // LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
+ // Lifecycles only (without ViewModel or LiveData)
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
+
+ //Activity
+ implementation "androidx.activity:activity-ktx:$activity_version"
+ //Fragment
+ implementation "androidx.fragment:fragment-ktx:$fragment_version"
+
+ //navigation
+ implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
+ implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
+
+ implementation 'com.google.gms:google-services:4.3.5'
+
+ //retrofit
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+
+ //signIN
+ implementation 'com.google.android.gms:play-services-drive:17.0.0'
+ implementation 'com.google.android.gms:play-services-auth:19.0.0'
+
+ //viewmodel
+ implementation "android.arch.lifecycle:extensions:1.1.1"
+
+ //glide
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
+
+ //roomDatabase
+ implementation 'androidx.room:room-ktx:2.3.0'
+ kapt "androidx.room:room-compiler:2.3.0"
+
+ implementation 'com.google.android.material:material:1.3.0'
+
+ implementation 'androidx.preference:preference-ktx:1.1.1'
+
+ // Koin main features for Android (Scope,ViewModel ...)
+ implementation "io.insert-koin:koin-android:$koin_version"
+ // Koin Android - experimental builder extensions
+ implementation "io.insert-koin:koin-android-ext:$koin_version"
+ // Koin for Jetpack WorkManager
+ implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
+ // Koin for Jetpack Compose (unstable version)
+ implementation "io.insert-koin:koin-androidx-compose:$koin_version"
+ // Koin for Kotlin Multiplatform
+ implementation "io.insert-koin:koin-core:$koin_version"
+ // Koin Test for Kotlin Multiplatform
+ testImplementation "io.insert-koin:koin-test:$koin_version"
+ // Koin for JUnit 4
+ testImplementation "io.insert-koin:koin-test-junit4:$koin_version"
+ // Koin for JUnit 5
+ testImplementation "io.insert-koin:koin-test-junit5:$koin_version"
+ // Koin Extended & experimental features (JVM)
+ implementation "io.insert-koin:koin-core-ext:$koin_version"
+
+ implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
+
+ // WorkManager
+ implementation "android.arch.work:work-runtime:1.0.1"
+
+ //FlexBox
+ implementation 'com.google.android.flexbox:flexbox:3.0.0'
+}
\ No newline at end of file
diff --git a/app/google-services.json b/app/google-services.json
new file mode 100644
index 00000000..ccd77ac6
--- /dev/null
+++ b/app/google-services.json
@@ -0,0 +1,93 @@
+{
+ "project_info": {
+ "project_number": "677958878281",
+ "firebase_url": "https://finema-b7bde-default-rtdb.europe-west1.firebasedatabase.app",
+ "project_id": "finema-b7bde",
+ "storage_bucket": "finema-b7bde.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:677958878281:android:abcc3c987d1a658a9deb98",
+ "android_client_info": {
+ "package_name": "Finema.app"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "677958878281-h8s3da3dpf41u4cbc0csu5t9iq6b250g.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "Finema.app",
+ "certificate_hash": "549d918079a35c978f5226f4a7ecd7d5bf250ae9"
+ }
+ },
+ {
+ "client_id": "677958878281-pmcs285i4cdvd5tob7c6ovd2j0msknvs.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyDO1vX5IwzD0tWU002zPXJuda5gseEPA38"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "677958878281-pmcs285i4cdvd5tob7c6ovd2j0msknvs.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:677958878281:android:59346e44ac0064a59deb98",
+ "android_client_info": {
+ "package_name": "com.example.finema"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "677958878281-bc2fulmg7en9sgv9ingij4sav2u4g35r.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.finema",
+ "certificate_hash": "08fa697c85845235d17c47709bceeae4bfe3692c"
+ }
+ },
+ {
+ "client_id": "677958878281-o32p6sa8b711am4ikvr2inhjql9en9us.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.finema",
+ "certificate_hash": "549d918079a35c978f5226f4a7ecd7d5bf250ae9"
+ }
+ },
+ {
+ "client_id": "677958878281-pmcs285i4cdvd5tob7c6ovd2j0msknvs.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyDO1vX5IwzD0tWU002zPXJuda5gseEPA38"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "677958878281-pmcs285i4cdvd5tob7c6ovd2j0msknvs.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/example/finema/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/finema/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..a64699d3
--- /dev/null
+++ b/app/src/androidTest/java/com/example/finema/ExampleInstrumentedTest.kt
@@ -0,0 +1,22 @@
+package com.example.finema
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.finema", appContext.packageName)
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..df380acc
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher1-playstore.png b/app/src/main/ic_launcher1-playstore.png
new file mode 100644
index 00000000..87931d13
Binary files /dev/null and b/app/src/main/ic_launcher1-playstore.png differ
diff --git a/app/src/main/ic_launcher2-playstore.png b/app/src/main/ic_launcher2-playstore.png
new file mode 100644
index 00000000..40b442bd
Binary files /dev/null and b/app/src/main/ic_launcher2-playstore.png differ
diff --git a/app/src/main/java/com/example/finema/MainActivity.kt b/app/src/main/java/com/example/finema/MainActivity.kt
new file mode 100644
index 00000000..b131d0a9
--- /dev/null
+++ b/app/src/main/java/com/example/finema/MainActivity.kt
@@ -0,0 +1,203 @@
+package com.example.finema
+
+import android.net.Uri
+import android.os.Bundle
+import android.preference.PreferenceManager
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.drawerlayout.widget.DrawerLayout
+import androidx.navigation.findNavController
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import com.example.finema.databinding.ActivityMainBinding
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.settings.NotificationService
+import com.example.finema.util.downloadAndSetImageUri
+import com.google.firebase.auth.FirebaseAuth
+import java.util.concurrent.TimeUnit
+import org.koin.android.ext.android.inject
+
+class MainActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityMainBinding
+ private val appPreference: IAppPreference by inject()
+ private var simpleNotification =
+ PeriodicWorkRequestBuilder(
+ NOTIFICATION_REPEAT,
+ TimeUnit.HOURS,
+ NOTIFICATION_FLEX,
+ TimeUnit.HOURS
+ )
+ .addTag("finema")
+ .build()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
+ super.onCreate(savedInstanceState)
+ appPreference.getPreference()
+
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.topAppBar.setNavigationOnClickListener {
+ binding.drawerLayout.open()
+ }
+
+ binding.navView.getHeaderView(HEADER_VIEW_INDEX).setOnClickListener {
+ binding.drawerLayout.close()
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_fragmentProfile)
+ }
+
+ binding.navView.setNavigationItemSelectedListener {
+ binding.drawerLayout.close()
+
+ when (it.itemId) {
+ R.id.lovely ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_fragmentFavourite)
+
+ R.id.budget ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_fragmentHigherLower)
+
+ R.id.rating ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_higherLowerRatingFragment)
+
+ R.id.tournament ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_fragmentTmp)
+
+ R.id.settings ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_fragmentSettings)
+
+ R.id.addFav ->
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_chooseFavouriteFragment)
+ }
+ true
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ addDestinationChangedListener()
+ checkIfUserInited()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+ val notifications = sharedPreferences.getBoolean("notifications", true)
+
+ if (notifications) {
+ WorkManager.getInstance(this)
+ .enqueueUniquePeriodicWork(
+ "notification",
+ ExistingPeriodicWorkPolicy.REPLACE,
+ simpleNotification
+ )
+ } else {
+ WorkManager.getInstance(this).cancelAllWorkByTag("finema")
+ }
+ }
+
+ private fun checkIfUserInited() {
+ if (!appPreference.getInitUser()) {
+ toSignInFragment()
+ } else {
+ if (FirebaseAuth.getInstance().currentUser?.displayName == null) {
+ setPhotoAndNameGuest()
+ } else {
+ setPhotoAndNameUser()
+ }
+ }
+ }
+
+ private fun addDestinationChangedListener() {
+ findNavController(R.id.fragment)
+ .addOnDestinationChangedListener { _, destination, _ ->
+ if (SCREENS_WITHOUT_DRAWER.contains(destination.id)) {
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ binding.topAppBar.visibility = View.GONE
+ binding.appBarCollapse.visibility = View.GONE
+ } else {
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED)
+ binding.topAppBar.visibility = View.VISIBLE
+ binding.appBarCollapse.visibility = View.VISIBLE
+ }
+
+ if (SCREENS_WITH_IMAGE.contains(destination.id)) {
+ binding.imageAppBar.visibility = View.VISIBLE
+ } else {
+ binding.imageAppBar.visibility = View.GONE
+ binding.imageAppBar.setImageDrawable(null)
+ binding.topAppBar.title = resources.getString(R.string.app_name)
+ }
+
+ when (destination.label) {
+ "Tournament fragment" -> {
+ appPreference.setFragment(destination.label as String)
+ }
+ "Film fragment" -> {
+ }
+
+ "SigInFragment" -> {
+ appPreference.setFragment(destination.label as String)
+ }
+
+ else -> {
+ appPreference.setFragment("")
+ Log.d("Destination", destination.label as String)
+ }
+ }
+ }
+ }
+
+ private fun setPhotoAndNameUser() {
+ binding.navView
+ .getHeaderView(HEADER_VIEW_INDEX)
+ .findViewById(R.id.nickProfile)
+ .text = FirebaseAuth.getInstance().currentUser?.displayName.orEmpty()
+ binding.navView
+ .getHeaderView(HEADER_VIEW_INDEX)
+ .findViewById(R.id.userAvatar)
+ .downloadAndSetImageUri(FirebaseAuth.getInstance().currentUser?.photoUrl)
+ }
+
+ private fun setPhotoAndNameGuest() {
+ binding.navView
+ .getHeaderView(HEADER_VIEW_INDEX)
+ .findViewById(R.id.nickProfile)
+ .text = "Гость"
+ binding.navView
+ .getHeaderView(HEADER_VIEW_INDEX)
+ .findViewById(R.id.userAvatar)
+ .downloadAndSetImageUri(Uri.parse(DEFAULT_URI))
+ }
+
+ private fun toSignInFragment() {
+ findNavController(R.id.fragment)
+ .navigate(R.id.action_global_signIn)
+ }
+
+ companion object {
+ private val SCREENS_WITHOUT_DRAWER = listOf(
+ R.id.sigInFragment
+ )
+ private val SCREENS_WITH_IMAGE = listOf(
+ R.id.fragmentFilm
+ )
+ private const val DEFAULT_URI =
+ "android.resource://com.example.finema/drawable/default_profile_avatar"
+ private const val NOTIFICATION_REPEAT = 12L
+ private const val NOTIFICATION_FLEX = 3L
+ private const val HEADER_VIEW_INDEX = 0
+ }
+}
diff --git a/app/src/main/java/com/example/finema/MovieApplication.kt b/app/src/main/java/com/example/finema/MovieApplication.kt
new file mode 100644
index 00000000..5e239960
--- /dev/null
+++ b/app/src/main/java/com/example/finema/MovieApplication.kt
@@ -0,0 +1,31 @@
+package com.example.finema
+
+import android.app.Application
+import com.example.finema.util.apiModule
+import com.example.finema.util.databaseModule
+import com.example.finema.util.repositoryModule
+import com.example.finema.util.viewModelModule
+import org.koin.android.ext.koin.androidContext
+import org.koin.android.ext.koin.androidFileProperties
+import org.koin.android.ext.koin.androidLogger
+import org.koin.core.context.startKoin
+
+class MovieApplication : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ startKoin {
+ androidLogger()
+ androidContext(this@MovieApplication)
+ androidFileProperties()
+ modules(
+ listOf(
+ apiModule,
+ databaseModule,
+ repositoryModule,
+ viewModelModule
+ )
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/api/IMoviesRepository.kt b/app/src/main/java/com/example/finema/api/IMoviesRepository.kt
new file mode 100644
index 00000000..2dbffb0e
--- /dev/null
+++ b/app/src/main/java/com/example/finema/api/IMoviesRepository.kt
@@ -0,0 +1,23 @@
+package com.example.finema.api
+
+import com.example.finema.models.genreRequest.GenreList
+import com.example.finema.models.movieResponse.MovieDetails
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.models.movieResponse.MovieResponseFromList
+import com.example.finema.models.movieResponse.TrailersList
+
+interface IMoviesRepository {
+ val api: MoviesApi
+
+ suspend fun getMovies(page: Int): MovieResponse
+
+ suspend fun getGenres(): GenreList
+
+ suspend fun getMoviesWithGenre(page: Int, withGenres: String): MovieResponse
+
+ suspend fun getTrailers(id: Long): TrailersList
+
+ suspend fun getMovieDetails(id: Long): MovieDetails
+
+ suspend fun getMovieFromList(listId: Int): MovieResponseFromList
+}
diff --git a/app/src/main/java/com/example/finema/api/MoviesApi.kt b/app/src/main/java/com/example/finema/api/MoviesApi.kt
new file mode 100644
index 00000000..21d9fb82
--- /dev/null
+++ b/app/src/main/java/com/example/finema/api/MoviesApi.kt
@@ -0,0 +1,75 @@
+package com.example.finema.api
+
+import com.example.finema.models.genreRequest.GenreList
+import com.example.finema.models.infinite.MovieDiscover
+import com.example.finema.models.movieResponse.MovieDetails
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.models.movieResponse.MovieResponseFromList
+import com.example.finema.models.movieResponse.TrailersList
+import retrofit2.Response
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface MoviesApi {
+
+ @GET("movie/popular?api_key=bbf5a3000e95f1dddf266b5e187d4b21&language=ru-Ru")
+ suspend fun getMovies(
+ @Query("page") page: Int
+ ): Response
+
+ @GET("movie/{movie_id}/videos?api_key=bbf5a3000e95f1dddf266b5e187d4b21")
+ suspend fun getTrailers(
+ @Path("movie_id") id: Long,
+ @Query("language") language: String
+ ): Response
+
+
+ @GET(GENRE_LIST)
+ suspend fun getGenreList(): Response
+
+ @GET("movie/{movie_id}?api_key=bbf5a3000e95f1dddf266b5e187d4b21")
+ suspend fun getMovieDetails(
+ @Path("movie_id") id: Long,
+ @Query("language") language: String
+ ): Response
+
+ @GET(TOP_RATED_LIST)
+ suspend fun getMoviesWithGenre(
+ @Query("page") page: Int,
+ @Query("withGenres") withGenres: String
+ ): Response
+
+ @GET("list/{listId}?$API_AND_LANGUAGE")
+ suspend fun getMovieFromList(
+ @Path("listId") listId: Int
+ ): Response
+
+ @GET(DISCOVER)
+ suspend fun everything(
+ @Query("page") page: Int,
+ @Query("query") query: String
+ ): Response
+
+ companion object {
+ operator fun invoke(): MoviesApi {
+ return Retrofit.Builder()
+ .addConverterFactory(GsonConverterFactory.create())
+ .baseUrl(BASE_URL)
+ .build()
+ .create(MoviesApi::class.java)
+ }
+ private const val API_AND_LANGUAGE =
+ "api_key=bbf5a3000e95f1dddf266b5e187d4b21&language=ru-ru"
+
+ private const val BASE_URL = "https://api.themoviedb.org/3/"
+ private const val GENRE_LIST =
+ "genre/movie/list?api_key=bbf5a3000e95f1dddf266b5e187d4b21&language=ru-ru"
+ private const val TOP_RATED_LIST =
+ "movie/top_rated?api_key=bbf5a3000e95f1dddf266b5e187d4b21&language=ru-Ru"
+ private const val DISCOVER =
+ "search/movie?api_key=bbf5a3000e95f1dddf266b5e187d4b21&language=ru-Ru"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/api/MoviesRepository.kt b/app/src/main/java/com/example/finema/api/MoviesRepository.kt
new file mode 100644
index 00000000..5a759fc6
--- /dev/null
+++ b/app/src/main/java/com/example/finema/api/MoviesRepository.kt
@@ -0,0 +1,33 @@
+package com.example.finema.api
+
+import com.example.finema.repositories.SafeApiRequest
+import java.util.Locale
+
+class MoviesRepository(
+ override val api: MoviesApi
+) : IMoviesRepository, SafeApiRequest {
+
+ override suspend fun getMovies(page: Int) = apiRequest {
+ api.getMovies(page)
+ }
+
+ override suspend fun getGenres() = apiRequest {
+ api.getGenreList()
+ }
+
+ override suspend fun getMoviesWithGenre(page: Int, withGenres: String) = apiRequest {
+ api.getMoviesWithGenre(page, withGenres)
+ }
+
+ override suspend fun getTrailers(id: Long) = apiRequest {
+ api.getTrailers(id, Locale.getDefault().toString().replace('_', '-'))
+ }
+
+ override suspend fun getMovieDetails(id: Long) = apiRequest {
+ api.getMovieDetails(id, Locale.getDefault().toString().replace('_', '-'))
+ }
+
+ override suspend fun getMovieFromList(listId: Int) = apiRequest {
+ api.getMovieFromList(listId)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/database/DatabaseRepository.kt b/app/src/main/java/com/example/finema/database/DatabaseRepository.kt
new file mode 100644
index 00000000..6e2e0897
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/DatabaseRepository.kt
@@ -0,0 +1,35 @@
+package com.example.finema.database
+
+import androidx.lifecycle.LiveData
+import com.example.finema.database.room.RoomDao
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.databaseModels.TopModel
+
+interface DatabaseRepository {
+ val roomDao: RoomDao
+
+ val allGenres: LiveData>
+
+ val allFavourites: LiveData>
+
+ val allTop: LiveData>
+
+ fun checkFavourite(movieId: List): LiveData>
+
+ suspend fun insert(genre: GenreModel, onSuccess: () -> Unit)
+
+ suspend fun insertTop(movie: TopModel, onSuccess: () -> Unit)
+
+ suspend fun insertFavourite(movie: MovieModel, onSuccess: () -> Unit)
+
+ suspend fun deleteAllFavourite(onSuccess: () -> Unit)
+
+ suspend fun deleteFavouriteMovie(movieId: Long, onSuccess: () -> Unit)
+
+ suspend fun deleteFavourite(movie: MovieModel, onSuccess: () -> Unit)
+
+ suspend fun deleteAllTop(onSuccess: () -> Unit)
+
+ fun signOut() {}
+}
diff --git a/app/src/main/java/com/example/finema/database/firebase/CategoriesLiveData.kt b/app/src/main/java/com/example/finema/database/firebase/CategoriesLiveData.kt
new file mode 100644
index 00000000..dbb5301b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/firebase/CategoriesLiveData.kt
@@ -0,0 +1,39 @@
+package com.example.finema.database.firebase
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import com.example.finema.models.databaseModels.CategoryModel
+import com.google.firebase.database.DataSnapshot
+import com.google.firebase.database.DatabaseError
+import com.google.firebase.database.FirebaseDatabase
+import com.google.firebase.database.ValueEventListener
+
+class CategoriesLiveData : LiveData>() {
+ private val refDatabaseCategory = FirebaseDatabase
+ .getInstance()
+ .reference
+ .child("category")
+
+ private val listener = object : ValueEventListener {
+
+ override fun onDataChange(p0: DataSnapshot) {
+ value = p0.children.map {
+ it.getValue(CategoryModel::class.java) ?: CategoryModel()
+ }
+ }
+
+ override fun onCancelled(error: DatabaseError) {
+ Log.d("CategoriesLiveData", error.message)
+ }
+ }
+
+ override fun onInactive() {
+ refDatabaseCategory.removeEventListener(listener)
+ super.onInactive()
+ }
+
+ override fun onActive() {
+ refDatabaseCategory.addValueEventListener(listener)
+ super.onActive()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/database/firebase/FirebaseRepository.kt b/app/src/main/java/com/example/finema/database/firebase/FirebaseRepository.kt
new file mode 100644
index 00000000..c5157689
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/firebase/FirebaseRepository.kt
@@ -0,0 +1,63 @@
+package com.example.finema.database.firebase
+
+import androidx.lifecycle.LiveData
+import com.example.finema.models.databaseModels.CategoryModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.database.DatabaseReference
+import com.google.firebase.database.FirebaseDatabase
+
+class FirebaseRepository(
+ override val firebaseDatabase: FirebaseDatabase
+) : IFirebaseRepository {
+ override val allCategories: LiveData> = CategoriesLiveData()
+ override val allMovies: LiveData> = MoviesFromFirebaseLiveData()
+
+ private val auth = FirebaseAuth.getInstance()
+ private val databaseReference = firebaseDatabase.reference
+ private lateinit var reDatabase: DatabaseReference
+ private lateinit var reDatabaseUser: DatabaseReference
+ private lateinit var reDatabaseUserSaved: DatabaseReference
+
+ override fun initRefCategory() {
+ reDatabase = databaseReference
+ .child("category")
+ }
+
+ override fun initRefs() {
+
+ val currentId = auth.currentUser?.uid.toString()
+ reDatabaseUser = databaseReference
+ .child("user_list")
+ .child(currentId)
+
+ reDatabaseUserSaved = reDatabaseUser.child("saved")
+ }
+
+ override fun insertFirebaseFavouriteFilm(movieModel: MovieModel) {
+
+ val fId = "idFirebase"
+ val fTitle = "title"
+ val fImageUrl = "imageUrl"
+ val fAbout = "about"
+ val fRating = "rating"
+
+ val mapMovie = hashMapOf()
+ mapMovie[fId] = movieModel.id.toString()
+ mapMovie[fTitle] = movieModel.title
+ mapMovie[fImageUrl] = movieModel.imageUrl.toString()
+ mapMovie[fAbout] = movieModel.about
+ mapMovie[fRating] = movieModel.rating
+
+ reDatabaseUserSaved.child(movieModel.id.toString())
+ .updateChildren(mapMovie)
+ }
+
+ override fun deleteFirebaseFavouriteFilm(movieModel: MovieModel) {
+ reDatabaseUserSaved.child(movieModel.id.toString()).removeValue()
+ }
+
+ override fun clearFirebaseFavourite() {
+ reDatabaseUserSaved.removeValue()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/database/firebase/IFirebaseRepository.kt b/app/src/main/java/com/example/finema/database/firebase/IFirebaseRepository.kt
new file mode 100644
index 00000000..39921563
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/firebase/IFirebaseRepository.kt
@@ -0,0 +1,22 @@
+package com.example.finema.database.firebase
+
+import androidx.lifecycle.LiveData
+import com.example.finema.models.databaseModels.CategoryModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.google.firebase.database.FirebaseDatabase
+
+interface IFirebaseRepository {
+ val firebaseDatabase: FirebaseDatabase
+ val allCategories: LiveData>
+ val allMovies: LiveData>
+
+ fun initRefCategory()
+
+ fun initRefs()
+
+ fun insertFirebaseFavouriteFilm(movieModel: MovieModel)
+
+ fun deleteFirebaseFavouriteFilm(movieModel: MovieModel)
+
+ fun clearFirebaseFavourite()
+}
diff --git a/app/src/main/java/com/example/finema/database/firebase/MoviesFromFirebaseLiveData.kt b/app/src/main/java/com/example/finema/database/firebase/MoviesFromFirebaseLiveData.kt
new file mode 100644
index 00000000..78e496dd
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/firebase/MoviesFromFirebaseLiveData.kt
@@ -0,0 +1,44 @@
+package com.example.finema.database.firebase
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import com.example.finema.models.databaseModels.MovieModel
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.database.DataSnapshot
+import com.google.firebase.database.DatabaseError
+import com.google.firebase.database.FirebaseDatabase
+import com.google.firebase.database.ValueEventListener
+
+class MoviesFromFirebaseLiveData : LiveData>() {
+
+ private val auth = FirebaseAuth.getInstance()
+ private val currentId = auth.currentUser?.uid.toString()
+ private val reDatabaseUserSaved = FirebaseDatabase
+ .getInstance()
+ .reference
+ .child("user_list")
+ .child(currentId)
+ .child("saved")
+
+ private val listener = object : ValueEventListener {
+ override fun onCancelled(p0: DatabaseError) {
+ Log.d("CategoriesLiveData", p0.message)
+ }
+
+ override fun onDataChange(p0: DataSnapshot) {
+ value = p0.children.map {
+ it.getValue(MovieModel::class.java) ?: MovieModel()
+ }
+ }
+ }
+
+ override fun onInactive() {
+ reDatabaseUserSaved.removeEventListener(listener)
+ super.onInactive()
+ }
+
+ override fun onActive() {
+ reDatabaseUserSaved.addValueEventListener(listener)
+ super.onActive()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/database/room/RoomDao.kt b/app/src/main/java/com/example/finema/database/room/RoomDao.kt
new file mode 100644
index 00000000..4b3a0999
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/room/RoomDao.kt
@@ -0,0 +1,48 @@
+package com.example.finema.database.room
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.databaseModels.TopModel
+
+// Data Access Object
+@Dao
+interface RoomDao {
+ @Query("SELECT * FROM genre_list")
+ fun getAllGenres(): LiveData>
+
+ @Query("SELECT * FROM favourite_list")
+ fun getAllFavourites(): LiveData>
+
+ @Query("SELECT * FROM top_list")
+ fun getAllTop(): LiveData>
+
+ @Query("SELECT id FROM favourite_list WHERE id IN (:movieId)")
+ fun checkFavourite(movieId: List): LiveData>
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insertFavourite(note: MovieModel)
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insert(note: GenreModel)
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insertTop(movie: TopModel)
+
+ @Query("DELETE FROM favourite_list WHERE id = :movieId;")
+ suspend fun deleteFavouriteMovie(movieId: Long)
+
+ @Query("DELETE FROM favourite_list;")
+ suspend fun deleteAllFavourite()
+
+ @Delete
+ suspend fun deleteFavourite(note: MovieModel)
+
+ @Query("DELETE FROM top_list;")
+ suspend fun deleteAllTop()
+}
diff --git a/app/src/main/java/com/example/finema/database/room/RoomDataBase.kt b/app/src/main/java/com/example/finema/database/room/RoomDataBase.kt
new file mode 100644
index 00000000..a4feef2b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/room/RoomDataBase.kt
@@ -0,0 +1,36 @@
+package com.example.finema.database.room
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.databaseModels.TopModel
+
+@Database(entities = [GenreModel::class, MovieModel::class, TopModel::class], version = 9)
+abstract class RoomDataBase : RoomDatabase() {
+
+ abstract fun getRoomDao(): RoomDao
+
+ companion object {
+
+ @Volatile
+ private var database: RoomDataBase? = null
+
+ @Synchronized
+ fun getInstance(context: Context): RoomDataBase {
+ return if (database == null) {
+ database = Room
+ .databaseBuilder(
+ context,
+ RoomDataBase::class.java,
+ "database"
+ )
+ .fallbackToDestructiveMigration().build()
+
+ database as RoomDataBase
+ } else database as RoomDataBase
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/database/room/RoomRepository.kt b/app/src/main/java/com/example/finema/database/room/RoomRepository.kt
new file mode 100644
index 00000000..031c2380
--- /dev/null
+++ b/app/src/main/java/com/example/finema/database/room/RoomRepository.kt
@@ -0,0 +1,53 @@
+package com.example.finema.database.room
+
+import androidx.lifecycle.LiveData
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.databaseModels.TopModel
+
+class RoomRepository(
+ override val roomDao: RoomDao
+) : DatabaseRepository {
+
+ override val allGenres: LiveData> = roomDao.getAllGenres()
+
+ override val allFavourites: LiveData> = roomDao.getAllFavourites()
+
+ override val allTop: LiveData> = roomDao.getAllTop()
+
+ override fun checkFavourite(movieId: List) = roomDao.checkFavourite(movieId)
+
+ override suspend fun insert(genre: GenreModel, onSuccess: () -> Unit) {
+ roomDao.insert(genre)
+ onSuccess()
+ }
+
+ override suspend fun insertTop(movie: TopModel, onSuccess: () -> Unit) {
+ roomDao.insertTop(movie)
+ }
+
+ override suspend fun insertFavourite(movie: MovieModel, onSuccess: () -> Unit) {
+ roomDao.insertFavourite(movie)
+ onSuccess()
+ }
+
+ override suspend fun deleteAllFavourite(onSuccess: () -> Unit) {
+ roomDao.deleteAllFavourite()
+ onSuccess()
+ }
+
+ override suspend fun deleteFavouriteMovie(movieId: Long, onSuccess: () -> Unit) {
+ roomDao.deleteFavouriteMovie(movieId)
+ onSuccess()
+ }
+
+ override suspend fun deleteFavourite(movie: MovieModel, onSuccess: () -> Unit) {
+ roomDao.deleteFavourite(movie)
+ onSuccess()
+ }
+
+ override suspend fun deleteAllTop(onSuccess: () -> Unit) {
+ roomDao.deleteAllTop()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/models/databaseModels/CategoryModel.kt b/app/src/main/java/com/example/finema/models/databaseModels/CategoryModel.kt
new file mode 100644
index 00000000..a560c9cb
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/databaseModels/CategoryModel.kt
@@ -0,0 +1,15 @@
+package com.example.finema.models.databaseModels
+
+import androidx.room.ColumnInfo
+import androidx.room.PrimaryKey
+
+data class CategoryModel(
+ @PrimaryKey
+ val id: String = "0",
+ @ColumnInfo
+ val name: String = "",
+ @ColumnInfo
+ val description: String = "",
+ @ColumnInfo
+ val link: String = "",
+)
diff --git a/app/src/main/java/com/example/finema/models/databaseModels/GenreModel.kt b/app/src/main/java/com/example/finema/models/databaseModels/GenreModel.kt
new file mode 100644
index 00000000..f4d76788
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/databaseModels/GenreModel.kt
@@ -0,0 +1,19 @@
+package com.example.finema.models.databaseModels
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.io.Serializable
+
+@Entity(tableName = "genre_list")
+data class GenreModel(
+ @PrimaryKey(autoGenerate = true)
+ val id: Int = 0,
+ @ColumnInfo
+ val name: String = "",
+ @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
+ var image: ByteArray? = null,
+
+ val idFirebase: String = ""
+
+) : Serializable
diff --git a/app/src/main/java/com/example/finema/models/databaseModels/MovieModel.kt b/app/src/main/java/com/example/finema/models/databaseModels/MovieModel.kt
new file mode 100644
index 00000000..a8cef70b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/databaseModels/MovieModel.kt
@@ -0,0 +1,32 @@
+package com.example.finema.models.databaseModels
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+import java.io.Serializable
+
+@Entity(tableName = "favourite_list", indices = [Index("id")])
+data class MovieModel(
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0,
+ @ColumnInfo
+ val title: String = "",
+// @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
+// var image: ByteArray? = null,
+ @ColumnInfo
+ val originalTitle: String = "",
+ @ColumnInfo
+ val imageUrl: String? = "",
+ @ColumnInfo
+ val about: String = "",
+ @ColumnInfo
+ val genres: String? = "",
+ @ColumnInfo
+ val rating: String = "",
+ @ColumnInfo
+ val companies: String? = "",
+
+ val idFirebase: String = "",
+
+) : Serializable
diff --git a/app/src/main/java/com/example/finema/models/databaseModels/TopModel.kt b/app/src/main/java/com/example/finema/models/databaseModels/TopModel.kt
new file mode 100644
index 00000000..eda22261
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/databaseModels/TopModel.kt
@@ -0,0 +1,25 @@
+package com.example.finema.models.databaseModels
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+import java.io.Serializable
+
+@Entity(tableName = "top_list", indices = [Index("id")])
+data class TopModel(
+ @PrimaryKey(autoGenerate = true)
+ val id: Long = 0,
+ @ColumnInfo
+ val title: String = "",
+ @ColumnInfo
+ val imageUrl: String? = "",
+ @ColumnInfo
+ val about: String = "",
+ @ColumnInfo
+ val genres: String? = "",
+ @ColumnInfo
+ val rating: String = "",
+ @ColumnInfo
+ val companies: String? = "",
+) : Serializable
diff --git a/app/src/main/java/com/example/finema/models/genreRequest/Genre.kt b/app/src/main/java/com/example/finema/models/genreRequest/Genre.kt
new file mode 100644
index 00000000..ee213e3b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/genreRequest/Genre.kt
@@ -0,0 +1,6 @@
+package com.example.finema.models.genreRequest
+
+data class Genre(
+ val id: Int,
+ val name: String
+)
diff --git a/app/src/main/java/com/example/finema/models/genreRequest/GenreList.kt b/app/src/main/java/com/example/finema/models/genreRequest/GenreList.kt
new file mode 100644
index 00000000..38a0d4de
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/genreRequest/GenreList.kt
@@ -0,0 +1,5 @@
+package com.example.finema.models.genreRequest
+
+data class GenreList(
+ val genres: List
+)
diff --git a/app/src/main/java/com/example/finema/models/infinite/MovieDiscover.kt b/app/src/main/java/com/example/finema/models/infinite/MovieDiscover.kt
new file mode 100644
index 00000000..cf453639
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/infinite/MovieDiscover.kt
@@ -0,0 +1,14 @@
+package com.example.finema.models.infinite
+
+import com.google.gson.annotations.SerializedName
+
+data class MovieDiscover(
+ @SerializedName("page")
+ val page: Int,
+ @SerializedName("results")
+ val results: List,
+ @SerializedName("total_pages")
+ val totalPages: Int,
+ @SerializedName("total_results")
+ val totalResults: Int
+)
diff --git a/app/src/main/java/com/example/finema/models/infinite/MovieDiscoverResult.kt b/app/src/main/java/com/example/finema/models/infinite/MovieDiscoverResult.kt
new file mode 100644
index 00000000..847b08ab
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/infinite/MovieDiscoverResult.kt
@@ -0,0 +1,34 @@
+package com.example.finema.models.infinite
+
+import com.google.gson.annotations.SerializedName
+
+data class MovieDiscoverResult(
+ @SerializedName("adult")
+ val adult: Boolean,
+ @SerializedName("backdrop_path")
+ val backdropPath: String,
+ @SerializedName("genre_ids")
+ val genreIds: List,
+ @SerializedName("id")
+ val id: Int,
+ @SerializedName("original_language")
+ val originalLanguage: String,
+ @SerializedName("original_title")
+ val originalTitle: String,
+ @SerializedName("overview")
+ val overview: String,
+ @SerializedName("popularity")
+ val popularity: Double,
+ @SerializedName("poster_path")
+ val posterPath: String,
+ @SerializedName("release_date")
+ val releaseDate: String,
+ @SerializedName("title")
+ val title: String,
+ @SerializedName("video")
+ val video: Boolean,
+ @SerializedName("vote_average")
+ val voteAverage: Double,
+ @SerializedName("vote_count")
+ val voteCount: Int
+)
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/Movie.kt b/app/src/main/java/com/example/finema/models/movieResponse/Movie.kt
new file mode 100644
index 00000000..ed0e273e
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/Movie.kt
@@ -0,0 +1,37 @@
+package com.example.finema.models.movieResponse
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class Movie(
+ @SerializedName("adult")
+ val adult: Boolean,
+ @SerializedName("backdrop_path")
+ val backdropPath: String,
+ @SerializedName("genre_ids")
+ val genreIds: List,
+ @SerializedName("id")
+ val id: Int,
+ @SerializedName("original_language")
+ val originalLanguage: String,
+ @SerializedName("original_title")
+ val originalTitle: String,
+ @SerializedName("overview")
+ val overview: String,
+ @SerializedName("popularity")
+ val popularity: Double,
+ @SerializedName("poster_path")
+ val posterPath: String,
+ @SerializedName("release_date")
+ val releaseDate: String,
+ @SerializedName("title")
+ val title: String,
+ @SerializedName("video")
+ val video: Boolean,
+ @SerializedName("vote_average")
+ val voteAverage: Double,
+ @SerializedName("vote_count")
+ val voteCount: Int,
+) : Parcelable
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/MovieDetails.kt b/app/src/main/java/com/example/finema/models/movieResponse/MovieDetails.kt
new file mode 100644
index 00000000..f066aa71
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/MovieDetails.kt
@@ -0,0 +1,60 @@
+package com.example.finema.models.movieResponse
+
+import com.example.finema.models.genreRequest.Genre
+import com.google.gson.annotations.SerializedName
+
+data class MovieDetails(
+ @SerializedName("adult")
+ val adult: Boolean,
+ @SerializedName("backdrop_path")
+ val backdropPath: String,
+// @SerializedName("belongs_to_collection")
+// val belongsToCollection : String,
+ @SerializedName("budget")
+ val budget: Int,
+ @SerializedName("genres")
+ val genres: List,
+ @SerializedName("homepage")
+ val homepage: String,
+ @SerializedName("id")
+ val id: Int,
+ @SerializedName("imdb_id")
+ val imdbId: String,
+ @SerializedName("original_language")
+ val originalLanguage: String,
+ @SerializedName("original_title")
+ val originalTitle: String,
+ @SerializedName("overview")
+ val overview: String,
+ @SerializedName("popularity")
+ val popularity: Double,
+ @SerializedName("poster_path")
+ var posterPath: String,
+ @SerializedName("production_companies")
+ val productionCompanies: List,
+// @SerializedName("production_countries")
+// val productionCountries : List,
+ @SerializedName("release_date")
+ val releaseDate: String,
+ @SerializedName("revenue")
+ val revenue: Int,
+ @SerializedName("runtime")
+ val runtime: Int,
+// @SerializedName("spoken_languages")
+// val spokenLanguages : List,
+ @SerializedName("status")
+ val status: String,
+ @SerializedName("tagline")
+ val tagline: String,
+ @SerializedName("title")
+ val title: String,
+ @SerializedName("video")
+ val video: Boolean,
+ @SerializedName("vote_average")
+ val voteAverage: Double,
+ @SerializedName("vote_count")
+ val voteCount: Int,
+
+ var stringGenres: String?,
+ var stringCompanies: String?
+)
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/MovieResponse.kt b/app/src/main/java/com/example/finema/models/movieResponse/MovieResponse.kt
new file mode 100644
index 00000000..291a721a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/MovieResponse.kt
@@ -0,0 +1,11 @@
+package com.example.finema.models.movieResponse
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class MovieResponse(
+ @SerializedName("results")
+ var movies: List
+) : Parcelable
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/MovieResponseFromList.kt b/app/src/main/java/com/example/finema/models/movieResponse/MovieResponseFromList.kt
new file mode 100644
index 00000000..b7995537
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/MovieResponseFromList.kt
@@ -0,0 +1,11 @@
+package com.example.finema.models.movieResponse
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class MovieResponseFromList(
+ @SerializedName("items")
+ var movies: List
+) : Parcelable
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/MovieTrailer.kt b/app/src/main/java/com/example/finema/models/movieResponse/MovieTrailer.kt
new file mode 100644
index 00000000..db4571e7
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/MovieTrailer.kt
@@ -0,0 +1,26 @@
+package com.example.finema.models.movieResponse
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class MovieTrailer(
+ @SerializedName("id")
+ val id: String,
+
+ @SerializedName("key")
+ val key: String,
+
+ @SerializedName("name")
+ val name: String,
+
+ @SerializedName("site")
+ val site: String,
+
+ @SerializedName("size")
+ val size: Int,
+
+ @SerializedName("type")
+ val type: String
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/ProductionCompanies.kt b/app/src/main/java/com/example/finema/models/movieResponse/ProductionCompanies.kt
new file mode 100644
index 00000000..9e88e95f
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/ProductionCompanies.kt
@@ -0,0 +1,14 @@
+package com.example.finema.models.movieResponse
+
+import com.google.gson.annotations.SerializedName
+
+data class ProductionCompanies(
+ @SerializedName("id")
+ val id: Int,
+ @SerializedName("logo_path")
+ val logoPath: String,
+ @SerializedName("name")
+ val name: String,
+ @SerializedName("origin_country")
+ val originCountry: String
+)
diff --git a/app/src/main/java/com/example/finema/models/movieResponse/TrailersList.kt b/app/src/main/java/com/example/finema/models/movieResponse/TrailersList.kt
new file mode 100644
index 00000000..78717a40
--- /dev/null
+++ b/app/src/main/java/com/example/finema/models/movieResponse/TrailersList.kt
@@ -0,0 +1,8 @@
+package com.example.finema.models.movieResponse
+
+import com.google.gson.annotations.SerializedName
+
+data class TrailersList(
+ @SerializedName("results")
+ val trailers: List
+)
diff --git a/app/src/main/java/com/example/finema/repositories/AppPreference.kt b/app/src/main/java/com/example/finema/repositories/AppPreference.kt
new file mode 100644
index 00000000..266926f1
--- /dev/null
+++ b/app/src/main/java/com/example/finema/repositories/AppPreference.kt
@@ -0,0 +1,142 @@
+package com.example.finema.repositories
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.util.Log
+import com.google.firebase.auth.FirebaseAuth
+
+// TODO Сделать репозиторием с интерфейсом
+class AppPreference(
+ override val context: Context
+) : IAppPreference {
+
+ companion object {
+ private const val INIT_USER = "initUser"
+ private const val NAME_PREF = "preference"
+ private const val NUM_FILMS = "numFilms"
+ private const val TOURNAMENT_TYPE = "tournamentType"
+ private const val GENRE_ID = "genre_id"
+ private const val GENRE_NAME = "genre_name"
+ private const val CATEGORY_LINK = "category_link"
+ private const val CATEGORY_NAME = "category_name"
+ private const val FRAGMENT = "fragment"
+ private const val FIRST_SIGN_IN = "firstSignIn"
+ private const val GUEST_OR_AUTH = "guestOrAuth"
+ private const val DEFAULT_NULL_VAL = ""
+
+ private const val DEFAULT_NUM_VAL = 8
+ private const val DEFAULT_NUM__CAT_VAL = -1
+ }
+
+ override val googleUser: FirebaseAuth = FirebaseAuth.getInstance()
+ override lateinit var mPreferences: SharedPreferences
+
+ override fun getPreference() {
+ mPreferences = context.getSharedPreferences(NAME_PREF, Context.MODE_PRIVATE)
+ }
+
+ override fun setInitUser(init: Boolean) {
+ mPreferences.edit()
+ .putBoolean(INIT_USER, init)
+ .apply()
+ }
+
+ override fun getInitUser(): Boolean {
+ return mPreferences.getBoolean(INIT_USER, false)
+ }
+
+ override fun googleUserSignOut() {
+ googleUser.signOut()
+ Log.d("OJOF", googleUser.currentUser?.displayName.orEmpty())
+ }
+
+ override fun getNumOfFilms(): Int {
+ return mPreferences.getInt(NUM_FILMS, DEFAULT_NUM_VAL)
+ }
+
+ override fun setNumOfFilms(num: Int) {
+ mPreferences.edit()
+ .putInt(NUM_FILMS, num)
+ .apply()
+ }
+
+ override fun getTournamentType(): String? {
+ return mPreferences.getString(TOURNAMENT_TYPE, DEFAULT_NULL_VAL)
+ }
+
+ override fun setTournamentType(type: String) {
+ mPreferences.edit()
+ .putString(TOURNAMENT_TYPE, type)
+ .apply()
+ }
+
+ override fun getGenreId(): String? {
+ return mPreferences.getString(GENRE_ID, DEFAULT_NULL_VAL)
+ }
+
+ override fun setGenre(genre: String) {
+ mPreferences.edit()
+ .putString(GENRE_ID, genre)
+ .apply()
+ }
+
+ override fun getGenreName(): String? {
+ return mPreferences.getString(GENRE_NAME, DEFAULT_NULL_VAL)
+ }
+
+ override fun setGenreName(genre: String) {
+ mPreferences.edit()
+ .putString(GENRE_NAME, genre)
+ .apply()
+ }
+
+ override fun getCategoryLink(): Int {
+ return mPreferences.getInt(CATEGORY_LINK, DEFAULT_NUM__CAT_VAL)
+ }
+
+ override fun setCategoryLink(categoryLink: Int) {
+ mPreferences.edit()
+ .putInt(CATEGORY_LINK, categoryLink)
+ .apply()
+ }
+
+ override fun getCategoryName(): String? {
+ return mPreferences.getString(CATEGORY_NAME, DEFAULT_NULL_VAL)
+ }
+
+ override fun setCategoryName(categoryName: String) {
+ mPreferences.edit()
+ .putString(CATEGORY_NAME, categoryName)
+ .apply()
+ }
+
+ override fun setFragment(name: String) {
+ mPreferences.edit()
+ .putString(FRAGMENT, name)
+ .apply()
+ }
+
+ override fun getFragment(): String? {
+ return mPreferences.getString(FRAGMENT, DEFAULT_NULL_VAL)
+ }
+
+ override fun setFirstSignIn(init: Boolean) {
+ mPreferences.edit()
+ .putBoolean(FIRST_SIGN_IN, init)
+ .apply()
+ }
+
+ override fun getFirstSignIn(): Boolean {
+ return mPreferences.getBoolean(FIRST_SIGN_IN, false)
+ }
+
+ override fun getGuestOrAuth(): String? {
+ return mPreferences.getString(GUEST_OR_AUTH, DEFAULT_NULL_VAL)
+ }
+
+ override fun setGuestOrAuth(name: String) {
+ mPreferences.edit()
+ .putString(GUEST_OR_AUTH, name)
+ .apply()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/repositories/Contract.kt b/app/src/main/java/com/example/finema/repositories/Contract.kt
new file mode 100644
index 00000000..d5be022f
--- /dev/null
+++ b/app/src/main/java/com/example/finema/repositories/Contract.kt
@@ -0,0 +1,96 @@
+package com.example.finema.repositories
+
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import androidx.activity.result.contract.ActivityResultContract
+import com.google.android.gms.auth.api.signin.GoogleSignIn
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount
+import com.google.android.gms.auth.api.signin.GoogleSignInClient
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.tasks.Task
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.auth.GoogleAuthProvider
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class Contract : ActivityResultContract() {
+
+ private val _name = MutableStateFlow("")
+ val name: StateFlow = _name.asStateFlow()
+
+ private val mAuth = FirebaseAuth.getInstance()
+ private lateinit var googleSignInClient: GoogleSignInClient
+
+ private val gso: GoogleSignInOptions = GoogleSignInOptions
+ .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestIdToken(defaultWebClientId)
+ .requestEmail()
+ .build()
+
+ fun setName(name: String) {
+ _name.value = name
+ }
+
+ fun signOut() {
+ if (this::googleSignInClient.isInitialized) {
+ googleSignInClient.signOut()
+ }
+ }
+
+ override fun createIntent(context: Context, input: Unit): Intent {
+ googleSignInClient = GoogleSignIn.getClient(context, gso)
+
+ return googleSignInClient.signInIntent
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?) {
+ activityResult(intent)
+ }
+
+ private fun activityResult(data: Intent?) {
+ val task: Task = GoogleSignIn.getSignedInAccountFromIntent(data)
+ val exception = task.exception
+ if (task.isSuccessful) {
+ try {
+ // Google Sign In was successful, authenticate with Firebase
+ val account: GoogleSignInAccount? = task.getResult(ApiException::class.java)
+ Log.d("Sign In Fragment", "firebaseAuthWithGoogle:" + account?.id)
+ firebaseAuthWithGoogle(account?.idToken!!)
+ } catch (e: ApiException) {
+ // Google Sign In failed, update UI appropriately
+ Log.w("Sign In Fragment", "Google sign in failed", e)
+ }
+ } else {
+ Log.w("Sign In Fragment", exception.toString())
+ }
+ }
+
+ private fun firebaseAuthWithGoogle(idToken: String) {
+ val credential = GoogleAuthProvider.getCredential(idToken, null)
+ mAuth.signInWithCredential(credential)
+ .addOnCompleteListener { task ->
+ if (task.isSuccessful) {
+ // Sign in success, update UI with the signed-in user's information
+ Log.d("firebaseAuthWithGoogle", "signInWithCredential:success")
+ val user = mAuth.currentUser
+// _name.tryEmit(user.displayName!!)
+ _name.value = user.displayName!!
+ Log.d("WOW", user.displayName!!)
+ } else {
+ // If sign in fails, display a message to the user.
+ Log.w(
+ "firebaseAuthWithGoogle",
+ "signInWithCredential:failure",
+ task.exception
+ )
+ }
+ }
+ }
+ companion object {
+ const val defaultWebClientId =
+ "677958878281-pmcs285i4cdvd5tob7c6ovd2j0msknvs.apps.googleusercontent.com"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/repositories/IAppPreference.kt b/app/src/main/java/com/example/finema/repositories/IAppPreference.kt
new file mode 100644
index 00000000..592af81d
--- /dev/null
+++ b/app/src/main/java/com/example/finema/repositories/IAppPreference.kt
@@ -0,0 +1,55 @@
+package com.example.finema.repositories
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.google.firebase.auth.FirebaseAuth
+
+interface IAppPreference {
+ val context: Context
+ val googleUser: FirebaseAuth
+ var mPreferences: SharedPreferences
+
+ fun getPreference()
+
+ fun setInitUser(init: Boolean)
+
+ fun getInitUser(): Boolean
+
+ fun googleUserSignOut()
+
+ fun getNumOfFilms(): Int
+
+ fun setNumOfFilms(num: Int)
+
+ fun getTournamentType(): String?
+
+ fun setTournamentType(type: String)
+
+ fun getGenreId(): String?
+
+ fun setGenre(genre: String)
+
+ fun getGenreName(): String?
+
+ fun setGenreName(genre: String)
+
+ fun getCategoryLink(): Int
+
+ fun setCategoryLink(categoryLink: Int)
+
+ fun getCategoryName(): String?
+
+ fun setCategoryName(categoryName: String)
+
+ fun setFragment(name: String)
+
+ fun getFragment(): String?
+
+ fun setFirstSignIn(init: Boolean)
+
+ fun getFirstSignIn(): Boolean
+
+ fun getGuestOrAuth(): String?
+
+ fun setGuestOrAuth(name: String)
+}
diff --git a/app/src/main/java/com/example/finema/repositories/SafeApiRequest.kt b/app/src/main/java/com/example/finema/repositories/SafeApiRequest.kt
new file mode 100644
index 00000000..b506855e
--- /dev/null
+++ b/app/src/main/java/com/example/finema/repositories/SafeApiRequest.kt
@@ -0,0 +1,19 @@
+package com.example.finema.repositories
+
+import java.io.IOException
+import retrofit2.Response
+
+interface SafeApiRequest {
+
+ suspend fun apiRequest(call: suspend () -> Response): T {
+ val response = call.invoke()
+ if (response.isSuccessful) {
+ return response.body()!!
+ } else {
+ // @todo handle api exception
+ throw ApiException(response.code().toString())
+ }
+ }
+}
+
+class ApiException(message: String) : IOException(message)
diff --git a/app/src/main/java/com/example/finema/ui/base/BaseFragment.kt b/app/src/main/java/com/example/finema/ui/base/BaseFragment.kt
new file mode 100644
index 00000000..038da631
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/base/BaseFragment.kt
@@ -0,0 +1,33 @@
+package com.example.finema.ui.base
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.observe
+import androidx.viewbinding.ViewBinding
+import java.lang.reflect.ParameterizedType
+
+abstract class BaseFragment : Fragment() {
+
+ protected open lateinit var viewModel: VModel
+
+ protected lateinit var binding: Binding
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ showError()
+ }
+
+ protected fun showError() {
+ viewModel.publicErrorMessage.observe(viewLifecycleOwner) {
+ Toast.makeText(context, it, Toast.LENGTH_LONG).show()
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun getViewModelClass(): Class {
+ val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
+ return type as Class
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/base/BaseViewModel.kt b/app/src/main/java/com/example/finema/ui/base/BaseViewModel.kt
new file mode 100644
index 00000000..51c11a13
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/base/BaseViewModel.kt
@@ -0,0 +1,18 @@
+package com.example.finema.ui.base
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.Job
+
+open class BaseViewModel : ViewModel() {
+
+ protected lateinit var job: Job
+
+ val publicErrorMessage: LiveData
+ get() = errorMessage
+
+ private val errorMessage = MutableLiveData()
+
+ fun showError(throwable: Throwable) = errorMessage.postValue("ERROR")
+}
diff --git a/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteFragment.kt b/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteFragment.kt
new file mode 100644
index 00000000..ec3925d3
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteFragment.kt
@@ -0,0 +1,68 @@
+package com.example.finema.ui.chooseFavourite
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.widget.doAfterTextChanged
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.Navigation
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.finema.R
+import com.example.finema.databinding.ChooseFavouriteFragmentBinding
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.hideKeyboard
+import kotlinx.coroutines.flow.collectLatest
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class ChooseFavouriteFragment :
+ BaseFragment(),
+ MovieAdapter.CharacterViewHolder.Listener {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = ChooseFavouriteFragmentBinding.inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(
+ view: View,
+ savedInstanceState: Bundle?
+ ) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ val adapterMovs = MovieAdapter(this)
+
+ binding.movs.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = adapterMovs
+ }
+
+ binding.query.doAfterTextChanged { text ->
+ viewModel.setQuery(text?.toString() ?: "")
+ }
+
+ viewLifecycleOwner.lifecycleScope.launchWhenCreated {
+ viewModel.movies.collectLatest { pagingData ->
+ adapterMovs.submitData(pagingData)
+ }
+ }
+ }
+
+ override fun onMovieClicked(index: Int) {
+ goDetailsFragment(index.toLong())
+ binding.query.hideKeyboard()
+ }
+
+ private fun goDetailsFragment(filmIdInfo: Long) {
+ val bundle = Bundle()
+ bundle.putSerializable("filmId", filmIdInfo)
+ Navigation.findNavController(requireActivity(), R.id.fragment)
+ .navigate(R.id.action_chooseFavouriteFragment_to_fragmentFilm, bundle)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteViewModel.kt b/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteViewModel.kt
new file mode 100644
index 00000000..8c3bc3d2
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/chooseFavourite/ChooseFavouriteViewModel.kt
@@ -0,0 +1,48 @@
+package com.example.finema.ui.chooseFavourite
+
+import androidx.lifecycle.viewModelScope
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.models.infinite.MovieDiscoverResult
+import com.example.finema.ui.base.BaseViewModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+class ChooseFavouriteViewModel(
+ private val repository: IMoviesRepository
+) : BaseViewModel() {
+
+ private val _query = MutableStateFlow("")
+ val query: StateFlow = _query.asStateFlow()
+
+ val movies: Flow> = query
+ .map(::newPager)
+ // TODO Попробовать с map
+ .flatMapLatest { pager -> pager.flow.cachedIn(viewModelScope) }
+ .stateIn(viewModelScope, SharingStarted.Lazily, PagingData.empty()) // TODO ??????
+
+ private fun newPager(query: String): Pager {
+ return Pager(
+ PagingConfig(
+ pageSize = 20,
+ maxSize = 100,
+ enablePlaceholders = false
+ )
+ ) {
+ MoviePagingSource(repository.api, query)
+ }
+ }
+
+ fun setQuery(query: String) {
+ _query.tryEmit(query)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/chooseFavourite/MovieAdapter.kt b/app/src/main/java/com/example/finema/ui/chooseFavourite/MovieAdapter.kt
new file mode 100644
index 00000000..b5eb652a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/chooseFavourite/MovieAdapter.kt
@@ -0,0 +1,72 @@
+package com.example.finema.ui.chooseFavourite
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.models.infinite.MovieDiscoverResult
+import com.example.finema.util.downloadAndSetImageUrl
+import com.google.android.material.card.MaterialCardView
+
+class MovieAdapter(
+ private val listener: CharacterViewHolder.Listener,
+) :
+ PagingDataAdapter(CharacterComparator) {
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): CharacterViewHolder {
+ return CharacterViewHolder(
+ LayoutInflater.from(parent.context).inflate(R.layout.nice, parent, false),
+ listener
+ )
+ }
+
+ override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
+ getItem(position)?.let { holder.bind(it) }
+ }
+
+ class CharacterViewHolder(
+ view: View,
+ private val listener: Listener
+ ) :
+ RecyclerView.ViewHolder(view) {
+ private val filmTitle: TextView = view.findViewById(R.id.tv_name)
+ private val movieCard: MaterialCardView = view.findViewById(R.id.movie_title)
+ private val moviePoster: ImageView = view.findViewById(R.id.imageViewNice)
+
+ interface Listener {
+ fun onMovieClicked(index: Int)
+ }
+
+ fun bind(item: MovieDiscoverResult) {
+ filmTitle.text = item.title
+ moviePoster.downloadAndSetImageUrl(
+ POSTER_BASE_URL + item.posterPath
+ )
+ movieCard.setOnClickListener {
+ listener.onMovieClicked(item.id)
+ }
+ }
+ }
+
+ object CharacterComparator : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: MovieDiscoverResult, newItem: MovieDiscoverResult) =
+ oldItem.id == newItem.id
+
+ override fun areContentsTheSame(
+ oldItem: MovieDiscoverResult,
+ newItem: MovieDiscoverResult
+ ) =
+ oldItem == newItem
+ }
+
+ companion object {
+ const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w342"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/chooseFavourite/MoviePagingSource.kt b/app/src/main/java/com/example/finema/ui/chooseFavourite/MoviePagingSource.kt
new file mode 100644
index 00000000..21f771db
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/chooseFavourite/MoviePagingSource.kt
@@ -0,0 +1,46 @@
+package com.example.finema.ui.chooseFavourite
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import com.example.finema.api.MoviesApi
+import com.example.finema.models.infinite.MovieDiscoverResult
+import java.lang.NullPointerException
+import retrofit2.HttpException
+
+class MoviePagingSource(
+ private val api: MoviesApi,
+ private val query: String
+) : PagingSource() {
+
+ override suspend fun load(params: LoadParams):
+ LoadResult {
+ return try {
+ val pageNumber = params.key ?: INITIAL_PAGE_NUMBER
+ val response = api.everything(pageNumber, query)
+
+ if (response.isSuccessful) {
+ val movies = response.body()!!.results
+ val nextPageNumber = if (movies.isEmpty()) null else pageNumber + 1
+ val prevPageNumber = if (pageNumber > 1) pageNumber - 1 else null
+ LoadResult.Page(movies, prevPageNumber, nextPageNumber)
+ } else {
+ LoadResult.Error(HttpException(response))
+ }
+ } catch (e: HttpException) {
+ LoadResult.Error(e)
+ } catch (e: NullPointerException) {
+ LoadResult.Error(e)
+ }
+ }
+
+ override fun getRefreshKey(state: PagingState): Int? {
+ return state.anchorPosition?.let { anchorPosition ->
+ val anchorPage = state.closestPageToPosition(anchorPosition)
+ anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
+ }
+ }
+
+ companion object {
+ const val INITIAL_PAGE_NUMBER = 1
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/favourite/FavouriteAdapter.kt b/app/src/main/java/com/example/finema/ui/favourite/FavouriteAdapter.kt
new file mode 100644
index 00000000..1a85388a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/favourite/FavouriteAdapter.kt
@@ -0,0 +1,57 @@
+package com.example.finema.ui.favourite
+
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.util.downloadAndSetImageUrl
+
+class FavouriteAdapter(
+ private val navigateToMovie: (Long) -> Unit
+) :
+ RecyclerView.Adapter() {
+
+ private var movies: List = emptyList()
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MovieViewHolder {
+ val view = LayoutInflater.from(viewGroup.context)
+ .inflate(R.layout.favourite_movie_item, viewGroup, false)
+
+ return MovieViewHolder(view)
+ }
+
+ override fun onBindViewHolder(viewholder: MovieViewHolder, position: Int) {
+ Log.d("gypsy", position.toString())
+ viewholder.bind(movies[position], navigateToMovie)
+ }
+
+ override fun getItemCount() = movies.size
+
+ fun update(movies: List) {
+ this.movies = movies
+ notifyDataSetChanged()
+ }
+
+ class MovieViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val imageMovie: ImageView = view.findViewById(R.id.imageMovie) as ImageView
+ val filmTitle: TextView = view.findViewById(R.id.filmTitle)
+ val rating: TextView = view.findViewById(R.id.rating)
+ val genre: TextView = view.findViewById(R.id.genre)
+
+ fun bind(movie: MovieModel, navigateToMovie: (Long) -> Unit) = itemView.apply {
+ imageMovie.downloadAndSetImageUrl(
+ movie.imageUrl
+ )
+ filmTitle.text = movie.title
+ rating.text = movie.rating
+ genre.text = movie.genres
+
+ setOnClickListener { navigateToMovie(movie.id) }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/favourite/FavouriteFragment.kt b/app/src/main/java/com/example/finema/ui/favourite/FavouriteFragment.kt
new file mode 100644
index 00000000..90cc1ddb
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/favourite/FavouriteFragment.kt
@@ -0,0 +1,74 @@
+package com.example.finema.ui.favourite
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.fragment.findNavController
+import com.example.finema.R
+import com.example.finema.databinding.FavouriteFragmentBinding
+import com.example.finema.ui.base.BaseFragment
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.flexbox.JustifyContent
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class FavouriteFragment : BaseFragment() {
+
+ private val favouriteAdapter = FavouriteAdapter {
+ val action = FavouriteFragmentDirections.actionFragmentFavouriteToFragmentFilm(it)
+ findNavController().navigate(action)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = FavouriteFragmentBinding.inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ content()
+
+ binding.choose.setOnClickListener {
+ findNavController().navigate(R.id.action_fragmentFavourite_to_chooseFavouriteFragment)
+ }
+ }
+
+ private fun content() {
+ viewModel.favouriteMovies.observe(
+ viewLifecycleOwner,
+ {
+ it?.let {
+ binding.searchLoader.visibility = View.INVISIBLE
+ binding.moviesList.visibility = View.VISIBLE
+ favouriteAdapter.update(it)
+
+ if (it.isEmpty()) {
+ binding.choose.visibility = View.VISIBLE
+ } else {
+ binding.choose.visibility = View.INVISIBLE
+ }
+
+ binding.moviesList.layoutManager = flexBox()
+ binding.moviesList.adapter = favouriteAdapter
+ }
+ }
+ )
+ }
+
+ private fun flexBox(): FlexboxLayoutManager {
+ val flex = FlexboxLayoutManager(context)
+ flex.flexDirection = FlexDirection.ROW
+ flex.alignItems = AlignItems.CENTER
+ flex.justifyContent = JustifyContent.CENTER
+ return flex
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/favourite/FavouriteViewModel.kt b/app/src/main/java/com/example/finema/ui/favourite/FavouriteViewModel.kt
new file mode 100644
index 00000000..2ae5966b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/favourite/FavouriteViewModel.kt
@@ -0,0 +1,20 @@
+package com.example.finema.ui.favourite
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.ui.base.BaseViewModel
+
+class FavouriteViewModel(
+ private val dbRepository: DatabaseRepository
+) : BaseViewModel() {
+
+ val favouriteMovies: LiveData> = dbRepository.allFavourites
+
+ init {
+ Log.d("gypsy", "DB")
+
+ Log.d("gypsy", favouriteMovies.value.toString())
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerFragment.kt b/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerFragment.kt
new file mode 100644
index 00000000..0f12cfde
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerFragment.kt
@@ -0,0 +1,134 @@
+package com.example.finema.ui.higherlower
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import com.example.finema.R
+import com.example.finema.databinding.HigherLowerFragmentBinding
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUrl
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class HigherLowerFragment : BaseFragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = HigherLowerFragmentBinding.inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ viewModel.movies.observe(
+ viewLifecycleOwner,
+ { movieList ->
+ if (viewModel.score == 0) {
+ viewModel.shuffle()
+ }
+ binding.txtFilm1.text = movieList.movies[viewModel.img1].title
+ binding.txtFilm2.text = movieList.movies[viewModel.img2].title
+ binding.points.text = getString(R.string.n_points, viewModel.score)
+
+ setImage(binding.img1, movieList, viewModel.img1)
+ setImage(binding.img2, movieList, viewModel.img2)
+
+ fillInBookmarks(binding.txtFilm1, binding.bookmark1)
+ fillInBookmarks(binding.txtFilm2, binding.bookmark2)
+ }
+ )
+ setBookmarkClickListeners(binding.bookmark1, binding.txtFilm1, 0)
+ setBookmarkClickListeners(binding.bookmark2, binding.txtFilm2, 1)
+ }
+
+ private fun setImage(image: ImageView, movieList: MovieResponse, imgInd: Int) {
+ image.downloadAndSetImageUrl(
+ getString(
+ R.string.poster_base_url,
+ movieList.movies[imgInd].posterPath
+ )
+ )
+ image.setOnClickListener {
+ viewModel.onMovieClicked(imgInd)
+ binding.points.text = getString(
+ R.string.higher_lower_score,
+ viewModel.score
+ )
+ }
+ }
+
+ private fun setBookmarkClickListeners(bookmark: ImageButton, title: TextView, position: Int) {
+ bookmark.setOnClickListener {
+ animateBookmark(bookmark)
+ addODelFav(title, position)
+ }
+ }
+
+ private fun fillInBookmarks(title: TextView, bookmark: ImageButton) {
+ viewModel.favouriteMovies.observe(
+ viewLifecycleOwner,
+ {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (title.text == i.title || title.text == i.originalTitle) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_24
+ )
+ break
+ }
+ if (counter == it.size) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_border_24
+ )
+ }
+ }
+ }
+ )
+ }
+
+ private fun addODelFav(title: TextView, position: Int) {
+ viewModel.favouriteMovies.value?.let {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (title.text == i.title) {
+ viewModel.removeFromFav(position)
+ break
+ }
+ if (it.size == counter) {
+ viewModel.addToFav(position)
+ }
+ }
+ }
+ }
+
+ private fun animateBookmark(bookmark: ImageButton) {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(ANIMATION_ROTATION)
+ scaleYBy(ANIMATION_ROTATION)
+ }.withEndAction {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(-ANIMATION_ROTATION)
+ scaleYBy(-ANIMATION_ROTATION)
+ }
+ }
+ }
+
+ companion object {
+ private const val ANIMATION_DURATION = 250L
+ private const val ANIMATION_ROTATION = 1f
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerViewModel.kt b/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerViewModel.kt
new file mode 100644
index 00000000..ccb85e19
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/higherlower/HigherLowerViewModel.kt
@@ -0,0 +1,161 @@
+package com.example.finema.ui.higherlower
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.movieResponse.Movie
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.example.finema.ui.tournaments.tournament.TournamentVM
+import com.example.finema.util.Coroutines
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class HigherLowerViewModel(
+ private val repository: IMoviesRepository,
+ private val dbRepository: DatabaseRepository,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ val favouriteMovies: LiveData> = dbRepository.allFavourites
+
+ private var _movies = MutableLiveData()
+ val movies: LiveData
+ get() = _movies
+
+ var score = RESET_SCORE_INDEX
+ var img1 = IMG1_INDEX
+ var img2 = IMG2_INDEX
+ private var page = DEFAULT_PAGE_INDEX
+
+ init {
+ getMovies()
+ }
+
+ fun shuffle() {
+ _movies.value?.movies.let {
+ _movies.value?.movies = it?.shuffled()!!
+ }
+ }
+
+ private fun getMovies() {
+ job = Coroutines.ioThenMan(
+ { repository.getMovies(page) },
+ { _movies.value = it }
+ )
+ }
+
+ private fun clickedRight() {
+ score += ADD_SCORE_POINT
+ changeMovRes()
+ }
+
+ private fun clickedWrong() {
+ score = RESET_SCORE_INDEX
+ page = DEFAULT_PAGE_INDEX
+ getMovies()
+ }
+
+ private fun changeMovRes() {
+ if (score % MOVIE_SIZE_RESET == 0) {
+ page += NEXT_PAGE
+ getMovies()
+ } else {
+ _movies.value?.movies = _movies.value?.movies?.drop(1)!!
+ _movies.value = _movies.value
+ }
+ }
+
+ fun onMovieClicked(position: Int) {
+ when (position) {
+ img1 ->
+ if (movies.value?.movies?.get(img1)?.popularity!!
+ >= movies.value?.movies?.get(img2)?.popularity!!
+ ) {
+ clickedRight()
+ } else {
+ clickedWrong()
+ }
+ img2 ->
+ if (movies.value?.movies?.get(img2)?.popularity!!
+ >= movies.value?.movies?.get(img1)?.popularity!!
+ ) {
+ clickedRight()
+ } else {
+ clickedWrong()
+ }
+ }
+ }
+
+ fun addToFav(position: Int) {
+ when (position) {
+ 0 -> {
+ insert(_movies.value?.movies?.get(0)!!)
+ }
+ 1 -> {
+ insert(_movies.value?.movies?.get(1)!!)
+ }
+ }
+ }
+
+ fun removeFromFav(position: Int) {
+ when (position) {
+ 0 -> {
+ delete(_movies.value?.movies?.get(0)!!)
+ }
+ 1 -> {
+ delete(_movies.value?.movies?.get(1)!!)
+ }
+ }
+ }
+
+ private fun insert(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(
+ makeMovieModel(movie)
+ ) {}
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ fbRepository.insertFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+ }
+
+ private fun delete(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.deleteFavourite(
+ makeMovieModel(movie)
+ ) {}
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ fbRepository.deleteFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+ }
+
+ private fun makeMovieModel(movie: Movie) =
+ MovieModel(
+ movie.id.toLong(),
+ movie.title,
+ movie.originalTitle,
+ TournamentVM.POSTER_BASE_URL + movie.posterPath,
+ movie.overview,
+ null,
+ movie.voteAverage.toString(),
+ null
+ )
+
+ companion object {
+ const val DEFAULT_PAGE_INDEX = 1
+ const val RESET_SCORE_INDEX = 0
+ const val ADD_SCORE_POINT = 1
+ const val NEXT_PAGE = 1
+ const val MOVIE_SIZE_RESET = 19
+ const val IMG1_INDEX = 0
+ const val IMG2_INDEX = 1
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingFragment.kt b/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingFragment.kt
new file mode 100644
index 00000000..00ae64e1
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingFragment.kt
@@ -0,0 +1,137 @@
+package com.example.finema.ui.higherlowerrating
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import com.example.finema.R
+import com.example.finema.databinding.HigherLowerRatingFragmentBinding
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUrl
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class HigherLowerRatingFragment :
+ BaseFragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = HigherLowerRatingFragmentBinding.inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ viewModel.movies.observe(
+ viewLifecycleOwner,
+ { movieList ->
+ if (viewModel.score == 0) {
+ viewModel.shuffle()
+ }
+ binding.txtFilm1.text = movieList.movies[viewModel.img1].title
+ binding.txtFilm2.text = movieList.movies[viewModel.img2].title
+ binding.points.text = getString(R.string.n_points, viewModel.score)
+ binding.txtRating.text = movieList.movies[viewModel.img1].voteAverage.toString()
+
+ setImage(binding.img1, movieList, viewModel.img1)
+ setImage(binding.img2, movieList, viewModel.img2)
+
+ fillInBookmarks(binding.txtFilm1, binding.bookmark1)
+ fillInBookmarks(binding.txtFilm2, binding.bookmark2)
+ }
+ )
+
+ setBookmarkClickListeners(binding.bookmark1, binding.txtFilm1, 0)
+ setBookmarkClickListeners(binding.bookmark2, binding.txtFilm2, 1)
+ }
+
+ private fun setImage(image: ImageView, movieList: MovieResponse, imgInd: Int) {
+ image.downloadAndSetImageUrl(
+ getString(
+ R.string.poster_base_url,
+ movieList.movies[imgInd].posterPath
+ )
+ )
+ image.setOnClickListener {
+ viewModel.onMovieClicked(imgInd)
+ binding.points.text = getString(
+ R.string.higher_lower_score,
+ viewModel.score
+ )
+ }
+ }
+
+ private fun setBookmarkClickListeners(bookmark: ImageButton, title: TextView, position: Int) {
+ bookmark.setOnClickListener {
+ animateBookmark(bookmark)
+ addODelFav(title, position)
+ }
+ }
+
+ private fun fillInBookmarks(txtview: TextView, bookmark: ImageButton) {
+ viewModel.favouriteMovies.observe(
+ viewLifecycleOwner,
+ {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (txtview.text == i.title) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_24
+ )
+ break
+ }
+ if (counter == it.size) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_border_24
+ )
+ }
+ }
+ }
+ )
+ }
+
+ private fun addODelFav(title: TextView, position: Int) {
+ viewModel.favouriteMovies.value?.let {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (title.text == i.title || title.text == i.originalTitle) {
+ viewModel.removeFromFav(position)
+ break
+ }
+ if (it.size == counter) {
+ viewModel.addToFav(position)
+ }
+ }
+ }
+ }
+
+ private fun animateBookmark(bookmark: ImageButton) {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(ANIMATION_ROTATION)
+ scaleYBy(ANIMATION_ROTATION)
+ }.withEndAction {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(-ANIMATION_ROTATION)
+ scaleYBy(-ANIMATION_ROTATION)
+ }
+ }
+ }
+
+ companion object {
+ private const val ANIMATION_DURATION = 250L
+ private const val ANIMATION_ROTATION = 1f
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingViewModel.kt b/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingViewModel.kt
new file mode 100644
index 00000000..5ce250c2
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/higherlowerrating/HigherLowerRatingViewModel.kt
@@ -0,0 +1,173 @@
+package com.example.finema.ui.higherlowerrating
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.movieResponse.Movie
+import com.example.finema.models.movieResponse.MovieResponse
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.example.finema.ui.tournaments.tournament.TournamentVM
+import com.example.finema.util.Coroutines
+import kotlin.random.Random
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class HigherLowerRatingViewModel(
+ private val repository: IMoviesRepository,
+ private val dbRepository: DatabaseRepository,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ val favouriteMovies: LiveData> = dbRepository.allFavourites
+
+ private var _movies = MutableLiveData()
+ val movies: LiveData
+ get() = _movies
+
+ var score = RESET_SCORE_INDEX
+ var img1 = IMG1_INDEX
+ var img2 = IMG2_INDEX
+ private var page = DEFAULT_PAGE_INDEX
+
+ init {
+ getMovies()
+ }
+
+ fun shuffle() {
+ _movies.value?.movies.let {
+ _movies.value?.movies = it?.shuffled()!!
+ }
+ }
+
+ private fun getMovies() {
+ job = Coroutines.ioThenMan(
+ { repository.getMovies(page) },
+ { _movies.value = it }
+ )
+ }
+
+ private fun clickedRight() {
+ score += ADD_SCORE_POINT
+ changeMovRes()
+ }
+
+ private fun clickedWrong() {
+ score = RESET_SCORE_INDEX
+ page = Random.nextInt(DEFAULT_PAGE_INDEX, LAST_PAGE)
+ getMovies()
+ }
+
+ private fun changeMovRes() {
+ if (score % MOVIE_SIZE_RESET == 0) {
+ page += NEXT_PAGE
+ getMovies()
+ } else {
+ _movies.value?.movies = _movies.value?.movies?.drop(1)!!
+ _movies.value = _movies.value
+ }
+ }
+
+ fun onMovieClicked(position: Int) {
+ when (position) {
+ img1 ->
+ if (movies.value?.movies?.get(img1)?.voteAverage!!
+ >= movies.value?.movies?.get(img2)?.voteAverage!!
+ ) {
+ clickedRight()
+ } else {
+ clickedWrong()
+ }
+ img2 ->
+ if (movies.value?.movies?.get(img2)?.voteAverage!!
+ >= movies.value?.movies?.get(img1)?.voteAverage!!
+ ) {
+ clickedRight()
+ } else {
+ clickedWrong()
+ }
+ }
+ }
+
+ fun addToFav(position: Int) {
+ when (position) {
+ 0 -> {
+ insert(_movies.value?.movies?.get(0)!!)
+ }
+ 1 -> {
+ insert(_movies.value?.movies?.get(1)!!)
+ }
+ }
+ }
+
+ fun removeFromFav(position: Int) {
+ when (position) {
+ 0 -> {
+ delete(_movies.value?.movies?.get(0)!!)
+ }
+ 1 -> {
+ delete(_movies.value?.movies?.get(1)!!)
+ }
+ }
+ }
+
+ private fun insert(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(
+ makeMovieModel(movie)
+ ) {
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ viewModelScope.launch(Dispatchers.Main) {
+ fbRepository.insertFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+ }
+
+ viewModelScope.launch(Dispatchers.Main) {
+ fbRepository.insertFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+
+ private fun delete(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.deleteFavourite(
+ makeMovieModel(movie)
+ ) {
+ }
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ viewModelScope.launch(Dispatchers.Main) {
+ fbRepository.deleteFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+ }
+
+ private fun makeMovieModel(movie: Movie) =
+ MovieModel(
+ movie.id.toLong(),
+ movie.title,
+ movie.originalTitle,
+ TournamentVM.POSTER_BASE_URL + movie.posterPath,
+ movie.overview,
+ null,
+ movie.voteAverage.toString(),
+ null
+ )
+
+ companion object {
+ const val DEFAULT_PAGE_INDEX = 1
+ const val RESET_SCORE_INDEX = 0
+ const val ADD_SCORE_POINT = 1
+ const val NEXT_PAGE = 1
+ const val MOVIE_SIZE_RESET = 19
+ const val IMG1_INDEX = 0
+ const val IMG2_INDEX = 1
+ const val LAST_PAGE = 500
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsFragment.kt b/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsFragment.kt
new file mode 100644
index 00000000..dcc13d21
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsFragment.kt
@@ -0,0 +1,184 @@
+package com.example.finema.ui.movieDetail
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.VISIBLE
+import android.view.View.INVISIBLE
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.Toast
+import androidx.lifecycle.Observer
+import com.example.finema.R
+import com.example.finema.databinding.MovieDetailsFragmentBinding
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.movieResponse.MovieDetails
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUrl
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class MovieDetailsFragment : BaseFragment() {
+
+ var movie: MovieModel = MovieModel()
+ var trailer: String? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = MovieDetailsFragmentBinding
+ .inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ initViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ viewModel.trailer.observe(viewLifecycleOwner, { item ->
+ trailer = item.key
+ if (item.key.isNotEmpty()) {
+ binding.trailer.visibility = VISIBLE
+ } else {
+ binding.trailer.visibility = INVISIBLE
+ }
+ })
+ viewModel.film.observe(viewLifecycleOwner, observerList)
+ viewModel.favouriteMovies.observe(viewLifecycleOwner, {})
+
+ binding.favourite.setOnClickListener {
+ addRemoveFavourite(movie.id)
+ }
+
+ binding.trailer.setOnClickListener {
+ if (trailer != null)
+ startActivity(
+ Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("http://www.youtube.com/watch?v=$trailer")
+ )
+ )
+ }
+ }
+
+ private val observerList: Observer = Observer {
+ binding.filmId = it
+
+ viewModel.film.value?.let { film ->
+ binding.genres.text = film.stringGenres
+
+ binding.companies.text = film.stringCompanies
+ }
+
+ val image = requireActivity().findViewById(R.id.imageAppBar)
+
+ image.downloadAndSetImageUrl(
+ POSTER_BASE_URL + it.posterPath
+ )
+
+ binding.rating.text = it.voteAverage.toString()
+
+ if (viewModel.favouriteMovies.value!!.contains(it.id.toLong())) {
+ binding.favourite.setImageResource(R.drawable.bookmark_24)
+ }
+
+ movie = MovieModel(
+ it.id.toLong(),
+ it.title,
+ it.originalTitle,
+ POSTER_BASE_URL + it.posterPath,
+ it.overview,
+ null,
+ it.voteAverage.toString(),
+ null
+ )
+
+ if (viewModel.getFragment() == "Tournament fragment") {
+ CoroutineScope(Dispatchers.IO).launch {
+ viewModel.addToTopMovies(
+ viewModel.toTopModel(movie)
+ )
+ }
+ }
+
+ afterLoading()
+ }
+
+ private fun initViewModel() {
+ viewModel = getViewModel()
+ viewModel.arg = requireArguments().getLong(KEY)
+ viewModel.getTrailer()
+ viewModel.getMovieDetails()
+ viewModel.checkFavourite()
+ }
+
+ private fun addRemoveFavourite(id: Long) {
+ viewModel.favouriteMovies.value?.let {
+ if (it.contains(id)) {
+ deleteFilm()
+ } else {
+ addFilm()
+ }
+ }
+ }
+
+ private fun deleteFilm() {
+ Toast.makeText(
+ context,
+ resources.getString(R.string.delete_from_favourite),
+ Toast.LENGTH_SHORT
+ ).show()
+ animateBookmark(binding.favourite)
+ binding.favourite.setImageResource(R.drawable.bookmark_border_24)
+ viewModel.deleteMovie(movie.id, movie)
+ }
+
+ private fun addFilm() {
+ Toast.makeText(
+ context,
+ resources.getString(R.string.add_to_favourite),
+ Toast.LENGTH_SHORT
+ ).show()
+ animateBookmark(binding.favourite)
+ binding.favourite.setImageResource(R.drawable.bookmark_24)
+ viewModel.insert(movie)
+ }
+
+ private fun animateBookmark(bookmark: ImageButton) {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(ANIMATION_ROTATION)
+ scaleYBy(ANIMATION_ROTATION)
+ }.withEndAction {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(-ANIMATION_ROTATION)
+ scaleYBy(-ANIMATION_ROTATION)
+ }
+ }
+ }
+
+ private fun afterLoading() {
+ binding.filmLoader.visibility = INVISIBLE
+ binding.aboutTitle.visibility = VISIBLE
+ binding.genreTitle.visibility = VISIBLE
+ binding.ratingTitle.visibility = VISIBLE
+ binding.companiesTitle.visibility = VISIBLE
+ binding.favourite.visibility = VISIBLE
+ }
+
+ companion object {
+ const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w342"
+ const val KEY = "filmId"
+ private const val ANIMATION_DURATION = 250L
+ private const val ANIMATION_ROTATION = 1f
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsViewModel.kt b/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsViewModel.kt
new file mode 100644
index 00000000..1445c4fb
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/movieDetail/MovieDetailsViewModel.kt
@@ -0,0 +1,121 @@
+package com.example.finema.ui.movieDetail
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.databaseModels.TopModel
+import com.example.finema.models.movieResponse.MovieDetails
+import com.example.finema.models.movieResponse.MovieTrailer
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.example.finema.util.Coroutines
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class MovieDetailsViewModel(
+ private val repository: IMoviesRepository,
+ private val dbRepository: DatabaseRepository,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ var film = MutableLiveData()
+ var trailer = MutableLiveData()
+ var arg: Long = 0
+ var favouriteMovies: LiveData> = dbRepository.checkFavourite(listOf(arg))
+
+ fun getMovieDetails() {
+ job = Coroutines.ioThenMan(
+ { repository.getMovieDetails(arg) },
+ {
+ film.value = it
+ film.value!!.stringGenres = ""
+ film.value!!.stringCompanies = ""
+
+ for (item in it!!.genres) {
+ film.value!!.stringGenres += item.name + NEW_LINE
+ }
+
+ for (item in it.productionCompanies) {
+ film.value!!.stringCompanies += item.name + TAB + item.originCountry + NEW_LINE
+ }
+
+ Log.d("gypsy", "Details")
+ }
+ )
+ }
+
+ fun getTrailer() {
+ job = Coroutines.ioThenMan(
+ { repository.getTrailers(arg) },
+ {
+ val smt = it?.trailers
+ Log.d("gypsy", "SMT " + smt?.size.toString())
+ smt?.forEach { item ->
+ if (item.site == "YouTube" && item.type == "Trailer") {
+ trailer.value = item
+ Log.d("gypsy", "TrailerYES")
+ }
+ }
+ }
+ )
+ job.start()
+ }
+
+ fun insert(movieModel: MovieModel) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(movieModel) {
+ }
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ viewModelScope.launch(Dispatchers.Main) {
+ fbRepository.insertFirebaseFavouriteFilm(movieModel)
+ }
+ }
+ }
+
+ fun checkFavourite() {
+ favouriteMovies = dbRepository.checkFavourite(listOf(arg))
+ }
+
+ fun toTopModel(movieModel: MovieModel): TopModel {
+ return TopModel(
+ movieModel.id,
+ movieModel.title,
+ movieModel.imageUrl,
+ movieModel.about,
+ movieModel.genres,
+ movieModel.rating,
+ movieModel.companies
+ )
+ }
+
+ fun deleteMovie(id: Long, movie: MovieModel) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.deleteFavouriteMovie(id) {}
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ viewModelScope.launch(Dispatchers.Main) {
+ fbRepository.deleteFirebaseFavouriteFilm(movie)
+ }
+ }
+ }
+
+ suspend fun addToTopMovies(movieModel: TopModel) {
+ dbRepository.insertTop(movieModel) {}
+ }
+
+ fun getFragment(): String? {
+ return appPreference.getFragment()
+ }
+
+ companion object {
+ const val NEW_LINE = "\n"
+ const val TAB = "\t"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/settings/NotificationService.kt b/app/src/main/java/com/example/finema/ui/settings/NotificationService.kt
new file mode 100644
index 00000000..1c2a2822
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/settings/NotificationService.kt
@@ -0,0 +1,49 @@
+package com.example.finema.ui.settings
+
+import android.R
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.TaskStackBuilder
+import android.content.Context
+import android.content.Context.NOTIFICATION_SERVICE
+import android.content.Intent
+import android.os.Build
+import androidx.core.app.NotificationCompat
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import com.example.finema.MainActivity
+
+class NotificationService(context: Context, workerParams: WorkerParameters) :
+ Worker(context, workerParams) {
+
+ override fun doWork(): Result {
+ sendNotification()
+ return Result.success()
+ }
+
+ private fun sendNotification() {
+ val notificationManager =
+ applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+ val resultIntent = Intent(applicationContext, MainActivity::class.java)
+ val resultPendingIntent = TaskStackBuilder.create(applicationContext)
+ .addParentStack(MainActivity::class.java)
+ .addNextIntent(resultIntent)
+ .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
+
+ val notification = NotificationCompat.Builder(applicationContext, "default")
+ .setContentTitle("Стало скучно?")
+ .setContentText("Выбери себе фильм на вечер")
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setSmallIcon(R.mipmap.sym_def_app_icon)
+ .setAutoCancel(true)
+ .setContentIntent(resultPendingIntent)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel =
+ NotificationChannel("default", "Default", NotificationManager.IMPORTANCE_DEFAULT)
+ notificationManager.createNotificationChannel(channel)
+ }
+ notificationManager.notify(0, notification.build())
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/finema/ui/settings/SettingsFragment.kt
new file mode 100644
index 00000000..da31c65b
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/settings/SettingsFragment.kt
@@ -0,0 +1,53 @@
+package com.example.finema.ui.settings
+
+import android.os.Bundle
+import android.widget.Toast
+import androidx.navigation.fragment.findNavController
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import com.example.finema.R
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class SettingsFragment : PreferenceFragmentCompat() {
+ private lateinit var viewModel: SettingsViewModel
+
+ private lateinit var notification: Preference
+ private lateinit var quit: Preference
+ private lateinit var clear: Preference
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ viewModel = getViewModel()
+ setPreferencesFromResource(R.xml.settings, rootKey)
+
+ quit = findPreference("quit")!!
+ clear = findPreference("clear_statistics")!!
+ notification = findPreference("notifications")!!
+
+ quit.onPreferenceClickListener =
+ Preference.OnPreferenceClickListener { // code for what you want it to do
+ viewModel.googleSignOut()
+ CoroutineScope(Dispatchers.IO).launch {
+ viewModel.clearAllStatistics()
+ }
+
+ findNavController().navigate(R.id.action_fragmentSettings_to_sigInFragment)
+ true
+ }
+
+ clear.onPreferenceClickListener =
+ Preference.OnPreferenceClickListener {
+ CoroutineScope(Dispatchers.IO).launch {
+ viewModel.clearAllStatistics()
+ if (viewModel.getGuestOrAuth() == "AUTH") {
+ viewModel.clearFireBase()
+ }
+ }
+
+ Toast.makeText(context, R.string.clear, Toast.LENGTH_SHORT).show()
+ true
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/example/finema/ui/settings/SettingsViewModel.kt
new file mode 100644
index 00000000..be2d691a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/settings/SettingsViewModel.kt
@@ -0,0 +1,34 @@
+package com.example.finema.ui.settings
+
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.repositories.Contract
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+
+class SettingsViewModel(
+ private val dbRepository: DatabaseRepository,
+ private val contract: Contract,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+
+) : BaseViewModel() {
+ suspend fun clearAllStatistics() {
+ dbRepository.deleteAllFavourite {}
+ dbRepository.deleteAllTop {}
+ }
+
+ fun clearFireBase() {
+ fbRepository.clearFirebaseFavourite()
+ }
+
+ fun googleSignOut() {
+ appPreference.setInitUser(false)
+ appPreference.googleUserSignOut()
+ contract.signOut()
+ }
+
+ fun getGuestOrAuth(): String? {
+ return appPreference.getGuestOrAuth()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/signIn/SigInFragment.kt b/app/src/main/java/com/example/finema/ui/signIn/SigInFragment.kt
new file mode 100644
index 00000000..2b3b3d17
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/signIn/SigInFragment.kt
@@ -0,0 +1,107 @@
+package com.example.finema.ui.signIn
+
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.activity.result.ActivityResultLauncher
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
+import com.example.finema.R
+import com.example.finema.databinding.SignInFragmentBinding
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUri
+import com.google.android.material.navigation.NavigationView
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class SigInFragment : BaseFragment() {
+
+ private lateinit var header: TextView
+ private lateinit var avatar: ImageView
+
+ private lateinit var customContract: ActivityResultLauncher
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = SignInFragmentBinding.inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ @InternalCoroutinesApi
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ finishActivityOnClickBack()
+ initCustomContract()
+
+ super.onViewCreated(view, savedInstanceState)
+
+ header = requireActivity().findViewById(R.id.nav_view)
+ .getHeaderView(0).findViewById(R.id.nickProfile)
+ avatar = requireActivity().findViewById(R.id.nav_view)
+ .getHeaderView(0).findViewById(R.id.userAvatar)
+
+ binding.signInWithGoogle.setOnClickListener {
+ binding.loader.visibility = View.VISIBLE
+ signIn()
+ viewModel.signInAuth()
+ }
+
+ binding.signInAsGuest.setOnClickListener {
+ viewModel.signInGuest()
+ header.text = resources.getText(R.string.guest)
+ avatar.downloadAndSetImageUri(Uri.parse(DEFAULT_URI))
+ findNavController().navigate(R.id.action_sigInFragment_to_tmpFragment)
+ }
+ }
+
+ @InternalCoroutinesApi
+ private fun signIn() {
+ customContract.launch(Unit)
+ }
+
+ @InternalCoroutinesApi
+ private fun initCustomContract() {
+ customContract = registerForActivityResult(viewModel.contract) {
+ lifecycleScope.launchWhenStarted {
+ viewModel.contract.name.collect { name ->
+ when (name) {
+ "" -> Unit
+ else -> letUserIn(name)
+ }
+ }
+ }
+ }
+ }
+
+ private fun letUserIn(name: String) {
+ Log.d("ID", viewModel.mAuth.currentUser?.uid.toString())
+ binding.loader.visibility = View.INVISIBLE
+ header.text = name
+ avatar.downloadAndSetImageUri(viewModel.mAuth.currentUser?.photoUrl)
+ findNavController().navigate(R.id.action_sigInFragment_to_tmpFragment)
+ }
+
+ private fun finishActivityOnClickBack() {
+ binding.root.isFocusableInTouchMode = true
+ binding.root.requestFocus()
+ binding.root.setOnKeyListener { _, _, _ ->
+ activity?.finish()
+ false
+ }
+ }
+
+ companion object {
+ private const val DEFAULT_URI =
+ "android.resource://com.example.finema/drawable/default_profile_avatar"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/signIn/SignInViewModel.kt b/app/src/main/java/com/example/finema/ui/signIn/SignInViewModel.kt
new file mode 100644
index 00000000..dbb1f3c0
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/signIn/SignInViewModel.kt
@@ -0,0 +1,29 @@
+package com.example.finema.ui.signIn
+
+import com.example.finema.repositories.Contract
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.google.firebase.auth.FirebaseAuth
+
+class SignInViewModel(
+ val contract: Contract,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ val mAuth = FirebaseAuth.getInstance()
+
+ init {
+ contract.setName("")
+ }
+
+ fun signInAuth() {
+ appPreference.setFirstSignIn(true)
+ appPreference.setInitUser(true)
+ appPreference.setGuestOrAuth("AUTH")
+ }
+
+ fun signInGuest() {
+ appPreference.setInitUser(true)
+ appPreference.setGuestOrAuth("GUEST")
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tmp/TmpFragment.kt b/app/src/main/java/com/example/finema/ui/tmp/TmpFragment.kt
new file mode 100644
index 00000000..71f7c0db
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tmp/TmpFragment.kt
@@ -0,0 +1,54 @@
+package com.example.finema.ui.tmp
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.fragment.findNavController
+import com.example.finema.R
+import com.example.finema.databinding.TmpFragmentBinding
+import com.example.finema.ui.base.BaseFragment
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class TmpFragment : BaseFragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = TmpFragmentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ if (viewModel.getFirstSignIn() && viewModel.getGuestOrAuth() == "AUTH") {
+ viewModel.initRoomFromFirebaseToRoom()
+ viewModel.setFirstSignIn(false)
+ }
+
+ super.onViewCreated(view, savedInstanceState)
+ binding.genre.setOnClickListener {
+ findNavController().navigate(R.id.action_fragment_tmp_to_fragment_genre)
+ viewModel.setTournamentType("GENRE")
+ }
+
+ binding.category.setOnClickListener {
+ findNavController().navigate(R.id.action_fragment_tmp_to_fragment_others)
+ viewModel.setTournamentType("CATEGORY")
+ }
+
+ binding.searchMovie.setOnClickListener {
+ findNavController().navigate(R.id.action_fragmentTmp_to_chooseFavouriteFragment)
+ }
+
+ binding.popularity.setOnClickListener {
+ findNavController().navigate(R.id.action_fragmentTmp_to_fragmentHigherLower)
+ }
+
+ binding.rating.setOnClickListener {
+ findNavController().navigate(R.id.action_fragmentTmp_to_higherLowerRatingFragment)
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tmp/TmpViewModel.kt b/app/src/main/java/com/example/finema/ui/tmp/TmpViewModel.kt
new file mode 100644
index 00000000..20225f7a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tmp/TmpViewModel.kt
@@ -0,0 +1,67 @@
+package com.example.finema.ui.tmp
+
+import androidx.lifecycle.Observer
+import androidx.lifecycle.viewModelScope
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class TmpViewModel(
+ private val dbRepository: DatabaseRepository,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ private val allMovies = fbRepository.allMovies
+
+ private lateinit var observerList: Observer>
+
+ init {
+ fbRepository.initRefs()
+ }
+
+ fun initRoomFromFirebaseToRoom() {
+ observerList = Observer { listMovie ->
+ listMovie.map { it.id = it.idFirebase.toLong() }
+ for (item in listMovie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(item) {}
+ }
+ }
+ }
+ allMovies.observeForever {
+ observerList
+ }
+ }
+
+ fun getFirstSignIn(): Boolean {
+ return appPreference.getFirstSignIn()
+ }
+
+ fun getGuestOrAuth(): String? {
+ return appPreference.getGuestOrAuth()
+ }
+
+ fun setFirstSignIn(boolean: Boolean) {
+ appPreference.setFirstSignIn(boolean)
+ }
+
+ fun setTournamentType(string: String) {
+ appPreference.setTournamentType(string)
+ }
+
+ fun insertFavourite(item: MovieModel) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(item) {}
+ }
+ }
+
+ override fun onCleared() {
+ allMovies.removeObserver(observerList)
+ super.onCleared()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentAdapter.kt b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentAdapter.kt
new file mode 100644
index 00000000..a4aff7f9
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentAdapter.kt
@@ -0,0 +1,64 @@
+package com.example.finema.ui.tournaments.categories
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.models.databaseModels.CategoryModel
+
+class CategoryTournamentAdapter(
+ private val listener: TournamentHolder.Listener,
+) : RecyclerView.Adapter() {
+
+ private var mListCategories = emptyList()
+
+ class TournamentHolder(
+ view: View,
+ private val listener: Listener
+ ) : RecyclerView.ViewHolder(view) {
+
+ private val categoryName: TextView = view.findViewById(R.id.item_category_name)
+ private val categoryDescription: TextView = view.findViewById(
+ R.id.item_category_description
+ )
+
+ interface Listener {
+ fun onMovieClicked(view: View, categoryModel: CategoryModel)
+ }
+
+ fun bind(name: String, description: String, mListCategories: List) {
+ categoryName.text = name
+ categoryDescription.text = description
+ itemView.setOnClickListener {
+ listener.onMovieClicked(itemView, mListCategories[adapterPosition])
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): TournamentHolder {
+
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.category_item, parent, false)
+ return TournamentHolder(view, listener)
+ }
+
+ override fun getItemCount(): Int = mListCategories.size
+
+ fun setList(list: List) {
+ mListCategories = list
+ notifyDataSetChanged()
+ }
+
+ override fun onBindViewHolder(holder: TournamentHolder, position: Int) {
+ holder.bind(
+ mListCategories[position].name,
+ mListCategories[position].description,
+ mListCategories
+ )
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentFragment.kt b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentFragment.kt
new file mode 100644
index 00000000..a8bab018
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentFragment.kt
@@ -0,0 +1,125 @@
+package com.example.finema.ui.tournaments.categories
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
+import androidx.lifecycle.Observer
+import androidx.navigation.Navigation
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.databinding.FragmentCategoryTournamentBinding
+import com.example.finema.models.databaseModels.CategoryModel
+import com.example.finema.ui.base.BaseFragment
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class CategoryTournamentFragment :
+ BaseFragment(),
+ CategoryTournamentAdapter.TournamentHolder.Listener {
+
+ private lateinit var recyclerView: RecyclerView
+ private lateinit var adapter: CategoryTournamentAdapter
+ private lateinit var observerList: Observer>
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = FragmentCategoryTournamentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+ initialization()
+ }
+
+ private fun initialization() {
+ adapter = CategoryTournamentAdapter(this)
+ recyclerView = binding.categoryRecycler
+ recyclerView.adapter = adapter
+ observerList = Observer {
+ val list = it
+ adapter.setList(list)
+ }
+ viewModel.allCategories.observe(requireActivity(), observerList)
+ }
+
+ override fun onMovieClicked(view: View, categoryModel: CategoryModel) {
+ if (categoryModel.link == CATEGORY_WARNING) {
+ warningDialog(categoryModel.link, categoryModel.name)
+ } else dialogBinding(categoryModel.link, categoryModel.name)
+ }
+
+ private fun warningDialog(link: String, categoryName: String) {
+ val builder = AlertDialog.Builder(requireContext())
+ builder.apply {
+ setTitle("Эта категория Содержит контент 18+")
+ builder.setMessage("Продолжить?")
+ setPositiveButton("Да") { _, _ ->
+ dialogBinding(link, categoryName)
+ }
+ setNegativeButton("Нет") { dialog, _ ->
+ dialog.cancel()
+ }
+ }
+ builder.create()
+ builder.show()
+ }
+
+ private fun dialogBinding(link: String, categoryName: String) {
+ // TODO Изменить на фрагмент
+ val dialog = Dialog(requireContext())
+ dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
+ dialog.window?.attributes?.windowAnimations = R.style.DialogAnimation
+ dialog.let {
+ it.setContentView(R.layout.number_fragment)
+ it.findViewById(R.id.btn8).setOnClickListener {
+ goNextFragment(PRESSED_EIGHT_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+
+ it.findViewById(R.id.btn16).setOnClickListener {
+ goNextFragment(PRESSED_SIXTEEN_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn32).setOnClickListener {
+ goNextFragment(PRESSED_THIRTY_TWO_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn64).setOnClickListener {
+ goNextFragment(PRESSED_SIXTY_FOUR_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn128).setOnClickListener {
+ goNextFragment(PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn256).setOnClickListener {
+ goNextFragment(PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES, link, categoryName)
+ dialog.onBackPressed()
+ }
+ }
+ dialog.show()
+ }
+
+ private fun goNextFragment(num: Int, link: String, categoryName: String) {
+ viewModel.setParameters(num, categoryName, link)
+ Navigation.findNavController(requireActivity(), R.id.fragment)
+ .navigate(R.id.action_fragmentOthers_to_fragmentTournament)
+ }
+
+ companion object {
+ const val PRESSED_EIGHT_MOVIES = 8
+ const val PRESSED_SIXTEEN_MOVIES = 16
+ const val PRESSED_THIRTY_TWO_MOVIES = 32
+ const val PRESSED_SIXTY_FOUR_MOVIES = 64
+ const val PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES = 128
+ const val PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES = 256
+ const val CATEGORY_WARNING = "133"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentVM.kt b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentVM.kt
new file mode 100644
index 00000000..67e6bb22
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/categories/CategoryTournamentVM.kt
@@ -0,0 +1,22 @@
+package com.example.finema.ui.tournaments.categories
+
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+
+class CategoryTournamentVM(
+ fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+ val allCategories = fbRepository.allCategories
+
+ init {
+ fbRepository.initRefCategory()
+ }
+
+ fun setParameters(num: Int, categoryName: String, link: String) {
+ appPreference.setNumOfFilms(num)
+ appPreference.setCategoryName(categoryName)
+ appPreference.setCategoryLink(link.toInt())
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentAdapter.kt b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentAdapter.kt
new file mode 100644
index 00000000..e2c3c42c
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentAdapter.kt
@@ -0,0 +1,52 @@
+package com.example.finema.ui.tournaments.genres
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.models.databaseModels.GenreModel
+
+class GenresTournamentAdapter(
+ private val listener: TournamentHolder.Listener,
+) : RecyclerView.Adapter() {
+
+ private var listGenres = emptyList()
+
+ fun setList(list: List) {
+ listGenres = list
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TournamentHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.genre_item, parent, false)
+ return TournamentHolder(view, listener)
+ }
+
+ override fun onBindViewHolder(holder: TournamentHolder, position: Int) {
+ holder.bind(listGenres[position].name, listGenres)
+ }
+
+ override fun getItemCount(): Int = listGenres.size
+
+ class TournamentHolder(
+ view: View,
+ private val listener: Listener
+ ) : RecyclerView.ViewHolder(view) {
+
+ private val genreName: TextView = view.findViewById(R.id.item_genre_name)
+
+ interface Listener {
+ fun onMovieClicked(view: View, genreModelId: GenreModel)
+ }
+
+ fun bind(name: String, mListGenres: List) {
+ genreName.text = name
+ itemView.setOnClickListener {
+ listener.onMovieClicked(itemView, mListGenres[adapterPosition])
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentFragment.kt b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentFragment.kt
new file mode 100644
index 00000000..d45c272e
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentFragment.kt
@@ -0,0 +1,128 @@
+package com.example.finema.ui.tournaments.genres
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.lifecycle.Observer
+import androidx.navigation.Navigation
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.databinding.FragmentTournamentGenresBinding
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.genreRequest.GenreList
+import com.example.finema.ui.base.BaseFragment
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class GenresTournamentFragment :
+ BaseFragment(),
+ GenresTournamentAdapter.TournamentHolder.Listener {
+
+ private lateinit var recyclerView: RecyclerView
+ private lateinit var adapter: GenresTournamentAdapter
+ private lateinit var observerList: Observer>
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = FragmentTournamentGenresBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+ initialization()
+ }
+
+ private fun initialization() {
+ if (!viewModel.checkDatabaseNotEmpty()) loadGenresList()
+
+ adapter = GenresTournamentAdapter(this)
+ recyclerView = binding.tournamentsRecycler
+ recyclerView.adapter = adapter
+
+ observerList = Observer {
+ adapter.setList(it)
+ }
+ // TODO Убрать получение и обращаться к VM базового класса
+ viewModel.allGenres.observe(requireActivity(), observerList)
+ }
+
+ // TODO genreModel -> {} : GenreModel -> Unit
+ // Заменить на лямбду, хз так ли написал выше
+ override fun onMovieClicked(view: View, genreModelId: GenreModel) {
+ if (genreModelId.id == GENRE_NOT_FOUND_TEMPORARILY) {
+ Toast.makeText(context, "Этот жанр временно отсутсвует", Toast.LENGTH_SHORT).show()
+ } else {
+ dialogBinding(genreModelId.id.toString(), genreModelId.name)
+ }
+ }
+
+ private fun dialogBinding(genreId: String, genreName: String) {
+ val dialog = Dialog(requireContext())
+ dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
+ dialog.window?.attributes?.windowAnimations = R.style.DialogAnimation
+ dialog.let {
+ it.setContentView(R.layout.number_fragment)
+ it.findViewById(R.id.btn8).setOnClickListener {
+ goNextFragment(PRESSED_EIGHT_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn16).setOnClickListener {
+ goNextFragment(PRESSED_SIXTEEN_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn32).setOnClickListener {
+ goNextFragment(PRESSED_THIRTY_TWO_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn64).setOnClickListener {
+ goNextFragment(PRESSED_SIXTY_FOUR_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn128).setOnClickListener {
+ goNextFragment(PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ it.findViewById(R.id.btn256).setOnClickListener {
+ goNextFragment(PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES, genreId, genreName)
+ dialog.onBackPressed()
+ }
+ }
+ dialog.show()
+ }
+
+ private fun goNextFragment(num: Int, genreId: String, genreName: String) {
+ viewModel.setParameters(num, genreName, genreId)
+ Navigation.findNavController(requireActivity(), R.id.fragment)
+ .navigate(R.id.action_fragmentGenre_to_fragmentTournament)
+ }
+
+ private fun loadGenresList() {
+ val observerList: Observer = Observer {
+ val list = it.genres
+ for (item in list) {
+ viewModel.insert(GenreModel(name = item.name, id = item.id))
+ }
+ }
+
+ viewModel.getGenres()
+
+ viewModel.genreListVM.observe(viewLifecycleOwner, observerList)
+ }
+
+ companion object {
+ const val GENRE_NOT_FOUND_TEMPORARILY = 99
+ const val PRESSED_EIGHT_MOVIES = 8
+ const val PRESSED_SIXTEEN_MOVIES = 16
+ const val PRESSED_THIRTY_TWO_MOVIES = 32
+ const val PRESSED_SIXTY_FOUR_MOVIES = 64
+ const val PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES = 128
+ const val PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES = 256
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentVM.kt b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentVM.kt
new file mode 100644
index 00000000..4b0c769f
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/genres/GenresTournamentVM.kt
@@ -0,0 +1,56 @@
+package com.example.finema.ui.tournaments.genres
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.models.databaseModels.GenreModel
+import com.example.finema.models.genreRequest.GenreList
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.example.finema.util.Coroutines
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class GenresTournamentVM(
+ private val apiRepository: IMoviesRepository,
+ private val dbRepository: DatabaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ val allGenres: LiveData>
+ get() {
+ return dbRepository.allGenres
+ }
+
+ var genreListVM = MutableLiveData()
+
+ fun checkDatabaseNotEmpty(): Boolean {
+ // TODO не работает, так как вызов асинхронный
+ return allGenres.value != null
+ }
+
+ fun signOut() {
+ dbRepository.signOut()
+ }
+
+ fun getGenres() {
+ job = Coroutines.ioThenMan(
+ { apiRepository.getGenres() },
+ { genreListVM.value = it }
+ )
+ }
+
+ fun insert(genreModel: GenreModel) =
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insert(genreModel) {
+ }
+ }
+
+ fun setParameters(num: Int, genreName: String, genreId: String) {
+ appPreference.setNumOfFilms(num)
+ appPreference.setGenreName(genreName)
+ appPreference.setGenre(genreId)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentFragment.kt b/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentFragment.kt
new file mode 100644
index 00000000..fc3d8af2
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentFragment.kt
@@ -0,0 +1,197 @@
+package com.example.finema.ui.tournaments.tournament
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.cardview.widget.CardView
+import androidx.navigation.fragment.findNavController
+import com.example.finema.R
+import com.example.finema.databinding.FragmentTournamentBinding
+import com.example.finema.models.movieResponse.Movie
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUrl
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class TournamentFragment : BaseFragment() {
+
+ private lateinit var desc: TextView
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = FragmentTournamentBinding
+ .inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+
+ super.onViewCreated(view, savedInstanceState)
+ viewModel.twoFilms.observe(
+ requireActivity(),
+ { movieList ->
+
+ binding.txtFilm1.text = movieList[0].title
+ binding.txtFilm2.text = movieList[1].title
+
+ binding.progressBar.visibility = View.GONE
+ binding.cardview1.visibility = View.VISIBLE
+ binding.cardview2.visibility = View.VISIBLE
+
+ setImage(binding.img1, movieList, 0)
+ setImage(binding.img2, movieList, 1)
+
+ binding.txtNumCategory.text = viewModel.title
+ binding.roundCount.text = getString(R.string.n_round, viewModel.roundCount)
+
+ cardClickListener(binding.cardview1, 0)
+ cardClickListener(binding.cardview2, 1)
+
+ infoClicked(binding.more1, 0)
+ infoClicked(binding.more2, 1)
+
+ fillInBookmarks(binding.txtFilm1, binding.bookmark1)
+ fillInBookmarks(binding.txtFilm2, binding.bookmark2)
+ }
+ )
+ setBookmarkClickListeners(binding.bookmark1, binding.txtFilm1, 0)
+ setBookmarkClickListeners(binding.bookmark2, binding.txtFilm2, 1)
+ }
+
+ private fun fillInBookmarks(txtview: TextView, bookmark: ImageButton) {
+ viewModel.favouriteMovies.observe(
+ viewLifecycleOwner,
+ {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (txtview.text == i.title) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_24
+ )
+ break
+ }
+ if (counter == it.size) {
+ bookmark.setImageResource(
+ R.drawable.bookmark_border_24
+ )
+ }
+ }
+ }
+ )
+ }
+
+ private fun infoClicked(button: ImageButton, position: Int) {
+ button.setOnClickListener {
+ dialogBinding(position)
+ }
+ }
+
+ private fun cardClickListener(cardView: CardView, position: Int) {
+ cardView.setOnClickListener {
+ itemClick(position)
+ }
+ }
+
+ private fun setImage(image: ImageView, movieList: List, imgInd: Int) {
+ image.downloadAndSetImageUrl(
+ getString(
+ R.string.poster_base_url,
+ movieList[imgInd].posterPath
+ )
+ )
+ }
+
+ private fun setBookmarkClickListeners(bookmark: ImageButton, title: TextView, position: Int) {
+ bookmark.setOnClickListener {
+ animateBookmark(bookmark)
+ addODelFav(title, position)
+ }
+ }
+
+ private fun dialogBinding(index: Int) {
+ val dialog = Dialog(requireContext())
+ dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
+ dialog.window?.attributes?.windowAnimations = R.style.DialogAnimation
+ dialog.let {
+ it.setContentView(R.layout.movie_description)
+ desc = it.findViewById(R.id.desc)
+ if (viewModel.returnDesc(index) == "") {
+ desc.text = "Пусто"
+ } else {
+ desc.text = viewModel.returnDesc(index)
+ }
+ }
+ dialog.show()
+ }
+
+ private fun addODelFav(title: TextView, position: Int) {
+ viewModel.favouriteMovies.value?.let {
+ var counter = 0
+ for (i in it) {
+ counter += 1
+ if (title.text == i.title || title.text == i.originalTitle) {
+ viewModel.removeFromFav(position)
+ break
+ }
+ if (it.size == counter) {
+ viewModel.addToFav(position)
+ }
+ }
+ }
+ }
+
+ private fun animateBookmark(bookmark: ImageButton) {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(ANIMATION_ROTATION)
+ scaleYBy(ANIMATION_ROTATION)
+ }.withEndAction {
+ bookmark.animate().apply {
+ duration = ANIMATION_DURATION
+ scaleXBy(-ANIMATION_ROTATION)
+ scaleYBy(-ANIMATION_ROTATION)
+ }
+ }
+ }
+
+ private fun itemClick(position: Int) {
+ (if (position == 0) viewModel.el1 else viewModel.el2)
+ .let {
+ if (viewModel.mainList.isEmpty()) {
+ if (viewModel.secondList.isEmpty()) {
+ val filmIdInfo = it.id.toLong()
+ goNextFragment(filmIdInfo)
+ } else {
+ viewModel.secondList.add(it)
+ viewModel.secondListToMainList()
+ viewModel.updateCards()
+ }
+ } else {
+ viewModel.secondList.add(it)
+ viewModel.updateCards()
+ }
+ }
+ }
+
+ private fun goNextFragment(filmIdInfo: Long) {
+ val bundle = Bundle()
+ bundle.putSerializable("filmId", filmIdInfo)
+ findNavController()
+ .navigate(R.id.action_fragment_tournament_to_fragment_film, bundle)
+ }
+
+ companion object {
+ private const val ANIMATION_DURATION = 250L
+ private const val ANIMATION_ROTATION = 1f
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentVM.kt b/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentVM.kt
new file mode 100644
index 00000000..5e47011d
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/tournaments/tournament/TournamentVM.kt
@@ -0,0 +1,258 @@
+package com.example.finema.ui.tournaments.tournament
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Observer
+import androidx.lifecycle.viewModelScope
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.models.databaseModels.MovieModel
+import com.example.finema.models.movieResponse.Movie
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.base.BaseViewModel
+import com.example.finema.util.Coroutines
+import kotlin.math.floor
+import kotlin.math.log
+import kotlin.math.pow
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class TournamentVM(
+ private val apiRepository: IMoviesRepository,
+ private val dbRepository: DatabaseRepository,
+ private val fbRepository: IFirebaseRepository,
+ private val appPreference: IAppPreference
+) : BaseViewModel() {
+
+ val favouriteMovies: LiveData> = dbRepository.allFavourites
+
+ var twoFilms = MutableLiveData>()
+ var loopNum: Int = ONE_LOOP
+ var flag: Int = FLAG_DEFAULT
+ var gotList = MutableLiveData>()
+ var mainList: ArrayList = ArrayList()
+ var secondList: ArrayList = ArrayList()
+
+ private lateinit var observerList: Observer>
+
+ lateinit var el1: Movie
+ lateinit var el2: Movie
+
+ var roundCount = ROUND_COUNT_START
+
+ var numFilms = PRESSED_EIGHT_MOVIES
+
+ var title = TITLE_DEFAULT
+
+ init {
+ start()
+ }
+
+ private fun start() {
+ numFilms = appPreference.getNumOfFilms()
+ genreOrCategory()
+ setLoopNum()
+ observerList = Observer {
+ numFilms = checkLessNum(it, numFilms)
+ mainList.addAll(it)
+ flag += 1
+
+ if (flag == loopNum) {
+ mainList = mainList.take(numFilms) as ArrayList
+ updateCards()
+ }
+ }
+ getMovies {
+ gotList.observeForever(observerList)
+ }
+ }
+
+ private fun getMovies(onSuccess: () -> Unit) {
+ when (appPreference.getTournamentType()) {
+ "GENRE" -> {
+ val genre = appPreference.getGenreId() ?: GENRE_ID_DEFAULT
+ for (page in 1..loopNum) {
+ job = Coroutines.ioThenMan(
+ { apiRepository.getMoviesWithGenre(page, genre) },
+ { gotList.value = it?.movies }
+ )
+ }
+ }
+ "CATEGORY" -> {
+ val categoryLink = appPreference.getCategoryLink()
+ for (page in 1..loopNum) {
+ job = Coroutines.ioThenMan(
+ { apiRepository.getMovieFromList(categoryLink) },
+ { gotList.value = it?.movies }
+ )
+ }
+ }
+ }
+ onSuccess()
+ }
+
+ fun secondListToMainList() {
+ mainList.addAll(secondList)
+ secondList.clear()
+ roundCount += 1
+ }
+
+ fun updateCards() {
+ el1 = mainList.random()
+ mainList.remove(el1)
+ el2 = mainList.random()
+ mainList.remove(el2)
+ twoFilms.value = listOf(el1, el2)
+ }
+
+ fun addToFav(position: Int) {
+ when (position) {
+ 0 -> {
+ insert(el1)
+ }
+ 1 -> {
+ insert(el2)
+ }
+ }
+ }
+
+ private fun endOfWordGrammar(): String {
+ var string = FILM_DECLENSION
+ when (numFilms) {
+ PRESSED_EIGHT_MOVIES -> string = "фильмов"
+ PRESSED_SIXTEEN_MOVIES -> string = "фильмов"
+ PRESSED_THIRTY_TWO_MOVIES -> string = "фильма"
+ PRESSED_SIXTY_FOUR_MOVIES -> string = "фильма"
+ PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES -> string = "фильмов"
+ PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES -> string = "фильма"
+ }
+ return string
+ }
+
+ private fun genreOrCategory() {
+ when (appPreference.getTournamentType()) {
+ "GENRE" -> {
+ val film = endOfWordGrammar()
+ val name = appPreference.getGenreName()
+ title = "$numFilms Лучших $film в жанре $name"
+ }
+ "CATEGORY" -> {
+ title = appPreference.getCategoryName() ?: " "
+ }
+ }
+ }
+
+ fun removeFromFav(position: Int) {
+ when (position) {
+ 0 -> {
+ delete(el1)
+ }
+ 1 -> {
+ delete(el2)
+ }
+ }
+ }
+
+ private fun insert(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.insertFavourite(
+ makeMovieModel(movie)
+ ) {
+ }
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ fbRepository.insertFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+
+ private fun delete(movie: Movie) {
+ viewModelScope.launch(Dispatchers.Main) {
+ dbRepository.deleteFavourite(
+ makeMovieModel(movie)
+ ) {
+ }
+ if (appPreference.getGuestOrAuth() == "AUTH") {
+ fbRepository.deleteFirebaseFavouriteFilm(makeMovieModel(movie))
+ }
+ }
+ }
+
+ private fun makeMovieModel(movie: Movie) =
+ MovieModel(
+ movie.id.toLong(),
+ movie.title,
+ movie.originalTitle,
+ POSTER_BASE_URL + movie.posterPath,
+ movie.overview,
+ null,
+ movie.voteAverage.toString(),
+ null
+ )
+
+ private fun setLoopNum() {
+ when (numFilms) {
+ PRESSED_EIGHT_MOVIES or PRESSED_SIXTEEN_MOVIES -> {
+ loopNum = ONE_LOOP
+ }
+ PRESSED_THIRTY_TWO_MOVIES -> {
+ loopNum = TWO_LOOP
+ }
+ PRESSED_SIXTY_FOUR_MOVIES -> {
+ loopNum = FOUR_LOOP
+ }
+ PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES -> {
+ loopNum = SEVEN_LOOP
+ }
+ PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES -> {
+ loopNum = THIRTEEN_LOOP
+ }
+ }
+ }
+
+ fun returnDesc(index: Int): String {
+ return when (index) {
+ 0 -> {
+ el1.overview
+ }
+ 1 -> {
+ el2.overview
+ }
+ else -> ""
+ }
+ }
+
+ private fun checkLessNum(list: List, num: Int): Int {
+ /* Если нажали на 32 фильма, а их всего 20, то мы округляем вниз число 20 до 16. */
+ var variable = num
+ if (list.size < num) {
+ variable = 2.0.pow(floor(log(list.size.toDouble(), 2.0))).toInt()
+ }
+ return variable
+ }
+
+ companion object {
+ const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w342"
+ const val PRESSED_EIGHT_MOVIES = 8
+ const val PRESSED_SIXTEEN_MOVIES = 16
+ const val PRESSED_THIRTY_TWO_MOVIES = 32
+ const val PRESSED_SIXTY_FOUR_MOVIES = 64
+ const val PRESSED_ONE_HUNDRED_AND_TWENTY_EIGHT_MOVIES = 128
+ const val PRESSED_TWO_HUNDRED_AND_FIFTY_SIX_MOVIES = 256
+ const val ROUND_COUNT_START = 1
+ const val ONE_LOOP = 1
+ const val TWO_LOOP = 2
+ const val FOUR_LOOP = 4
+ const val SEVEN_LOOP = 7
+ const val THIRTEEN_LOOP = 13
+ const val FLAG_DEFAULT = 0
+ const val TITLE_DEFAULT = ""
+ const val GENRE_ID_DEFAULT = "12"
+ const val FILM_DECLENSION = "фильмов"
+ }
+
+ override fun onCleared() {
+ gotList.removeObserver(observerList)
+ super.onCleared()
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/userProfile/ProfileAdapter.kt b/app/src/main/java/com/example/finema/ui/userProfile/ProfileAdapter.kt
new file mode 100644
index 00000000..88bf62e2
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/userProfile/ProfileAdapter.kt
@@ -0,0 +1,59 @@
+package com.example.finema.ui.userProfile
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.finema.R
+import com.example.finema.models.databaseModels.TopModel
+import com.example.finema.util.downloadAndSetImageUrl
+
+class ProfileAdapter(
+ private val listener: ProfileHolder.Listener
+) : RecyclerView.Adapter() {
+ var movies: List = emptyList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.top_movie_item, parent, false)
+
+ return ProfileHolder(view, listener)
+ }
+
+ override fun onBindViewHolder(holder: ProfileHolder, position: Int) {
+ holder.bind(position, movies)
+ }
+
+ override fun getItemCount(): Int {
+ return movies.size
+ }
+
+ class ProfileHolder(
+ view: View,
+ private val listener: Listener
+ ) : RecyclerView.ViewHolder(view) {
+
+ private val movieName: TextView = view.findViewById(R.id.top_title)
+ private val moviePoster: ImageView = view.findViewById(R.id.posterTop)
+
+ interface Listener {
+ fun onMovieClicked(movie: TopModel)
+ }
+
+ fun bind(position: Int, listMovie: List) {
+ movieName.text = listMovie[position].title
+ moviePoster.downloadAndSetImageUrl(
+ POSTER_BASE_URL + listMovie[adapterPosition].imageUrl
+ )
+ itemView.setOnClickListener {
+ listener.onMovieClicked(listMovie[adapterPosition])
+ }
+ }
+ }
+
+ companion object {
+ const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w342"
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/userProfile/ProfileFragment.kt b/app/src/main/java/com/example/finema/ui/userProfile/ProfileFragment.kt
new file mode 100644
index 00000000..bb6f176a
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/userProfile/ProfileFragment.kt
@@ -0,0 +1,65 @@
+package com.example.finema.ui.userProfile
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.Navigation
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.finema.R
+import com.example.finema.databinding.ProfileFragmentBinding
+import com.example.finema.models.databaseModels.TopModel
+import com.example.finema.ui.base.BaseFragment
+import com.example.finema.util.downloadAndSetImageUri
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class ProfileFragment :
+ BaseFragment(),
+ ProfileAdapter.ProfileHolder.Listener {
+
+ private lateinit var profileAdapter: ProfileAdapter
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = ProfileFragmentBinding
+ .inflate(inflater, container, false)
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel = getViewModel()
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.userName.text = viewModel.getName()
+ binding.userPhone.text = viewModel.getNumber()
+ binding.Avatar.downloadAndSetImageUri(viewModel.getImage())
+
+ profileAdapter = ProfileAdapter(this)
+ viewModel.topMovies.observe(
+ viewLifecycleOwner,
+ {
+ it?.let {
+ binding.topRecycler.visibility = View.VISIBLE
+ profileAdapter.movies = it
+ binding.topRecycler.layoutManager = LinearLayoutManager(context)
+ binding.topRecycler.adapter = profileAdapter
+ }
+ }
+ )
+ }
+
+ override fun onMovieClicked(movie: TopModel) {
+ goDetailsFragment(movie.id)
+ }
+
+ private fun goDetailsFragment(filmIdInfo: Long) {
+ val bundle = Bundle()
+ bundle.putSerializable("filmId", filmIdInfo)
+ Navigation.findNavController(requireActivity(), R.id.fragment)
+ .navigate(R.id.action_fragment_profile_to_fragment_film, bundle)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/ui/userProfile/ProfileViewModel.kt b/app/src/main/java/com/example/finema/ui/userProfile/ProfileViewModel.kt
new file mode 100644
index 00000000..9196796e
--- /dev/null
+++ b/app/src/main/java/com/example/finema/ui/userProfile/ProfileViewModel.kt
@@ -0,0 +1,41 @@
+package com.example.finema.ui.userProfile
+
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.models.databaseModels.TopModel
+import com.example.finema.ui.base.BaseViewModel
+import com.google.firebase.auth.FirebaseAuth
+
+class ProfileViewModel(
+ private val dbRepository: DatabaseRepository
+) : BaseViewModel() {
+
+ private val mAuth = FirebaseAuth.getInstance()
+ val topMovies: LiveData> = dbRepository.allTop
+
+ fun getName(): String? {
+ return if (mAuth.currentUser?.displayName == null) {
+ "Гость"
+ } else {
+ mAuth.currentUser?.displayName
+ }
+ }
+
+ fun getNumber(): String? {
+ return if (mAuth.currentUser?.phoneNumber == null) {
+ " "
+ } else {
+ mAuth.currentUser?.phoneNumber
+ }
+ }
+
+ fun getImage(): Uri? {
+
+ return if (mAuth.currentUser?.photoUrl == null) {
+ Uri.parse("android.resource://com.example.finema/drawable/default_profile_avatar")
+ } else {
+ mAuth.currentUser?.photoUrl
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/finema/util/AppModule.kt b/app/src/main/java/com/example/finema/util/AppModule.kt
new file mode 100644
index 00000000..2fc86b89
--- /dev/null
+++ b/app/src/main/java/com/example/finema/util/AppModule.kt
@@ -0,0 +1,84 @@
+package com.example.finema.util
+
+import com.example.finema.api.IMoviesRepository
+import com.example.finema.api.MoviesApi
+import com.example.finema.api.MoviesRepository
+import com.example.finema.database.DatabaseRepository
+import com.example.finema.database.firebase.FirebaseRepository
+import com.example.finema.database.firebase.IFirebaseRepository
+import com.example.finema.database.room.RoomDataBase
+import com.example.finema.database.room.RoomRepository
+import com.example.finema.repositories.AppPreference
+import com.example.finema.repositories.Contract
+import com.example.finema.repositories.IAppPreference
+import com.example.finema.ui.chooseFavourite.ChooseFavouriteViewModel
+import com.example.finema.ui.favourite.FavouriteViewModel
+import com.example.finema.ui.higherlower.HigherLowerViewModel
+import com.example.finema.ui.higherlowerrating.HigherLowerRatingViewModel
+import com.example.finema.ui.movieDetail.MovieDetailsViewModel
+import com.example.finema.ui.settings.SettingsViewModel
+import com.example.finema.ui.signIn.SignInViewModel
+import com.example.finema.ui.tmp.TmpViewModel
+import com.example.finema.ui.tournaments.categories.CategoryTournamentVM
+import com.example.finema.ui.tournaments.genres.GenresTournamentVM
+import com.example.finema.ui.tournaments.tournament.TournamentVM
+import com.example.finema.ui.userProfile.ProfileViewModel
+import com.google.firebase.database.FirebaseDatabase
+import org.koin.android.ext.koin.androidContext
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.module.Module
+import org.koin.dsl.module
+
+val viewModelModule: Module = module {
+
+ viewModel { HigherLowerViewModel(get(), get(), get(), get()) }
+
+ viewModel { SignInViewModel(get(), get()) }
+
+ viewModel { TmpViewModel(get(), get(), get()) }
+
+ viewModel { GenresTournamentVM(get(), get(), get()) }
+
+ viewModel { MovieDetailsViewModel(get(), get(), get(), get()) }
+
+ viewModel { TournamentVM(get(), get(), get(), get()) }
+
+ viewModel { CategoryTournamentVM(get(), get()) }
+
+ viewModel { HigherLowerRatingViewModel(get(), get(), get(), get()) }
+
+ viewModel { FavouriteViewModel(get()) }
+
+ viewModel { SettingsViewModel(get(), get(), get(), get()) }
+
+ viewModel { ChooseFavouriteViewModel(get()) }
+
+ viewModel { ProfileViewModel(get()) }
+}
+
+val apiModule: Module = module {
+
+ single { MoviesApi() }
+}
+
+val databaseModule: Module = module {
+
+ single { RoomDataBase.getInstance(androidContext()) }
+
+ single { RoomDataBase.getInstance(androidContext()).getRoomDao() }
+
+ single { FirebaseDatabase.getInstance() }
+}
+
+val repositoryModule: Module = module {
+
+ single { Contract() }
+
+ single { AppPreference(androidContext()) }
+
+ single { RoomRepository(get()) }
+
+ single { MoviesRepository(get()) }
+
+ single { FirebaseRepository(get()) }
+}
diff --git a/app/src/main/java/com/example/finema/util/Coroutines.kt b/app/src/main/java/com/example/finema/util/Coroutines.kt
new file mode 100644
index 00000000..a9deb19f
--- /dev/null
+++ b/app/src/main/java/com/example/finema/util/Coroutines.kt
@@ -0,0 +1,17 @@
+package com.example.finema.util
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+
+object Coroutines {
+
+ fun ioThenMan(work: suspend (() -> T?), callback: ((T?) -> Unit)) =
+ CoroutineScope(Dispatchers.Main).launch {
+ val data = CoroutineScope(Dispatchers.IO).async rt@{
+ return@rt work()
+ }.await()
+ callback(data)
+ }
+}
diff --git a/app/src/main/java/com/example/finema/util/funs.kt b/app/src/main/java/com/example/finema/util/funs.kt
new file mode 100644
index 00000000..2d914133
--- /dev/null
+++ b/app/src/main/java/com/example/finema/util/funs.kt
@@ -0,0 +1,35 @@
+package com.example.finema.util
+import android.content.Context
+import android.net.Uri
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import android.widget.ImageView
+import com.bumptech.glide.Glide
+import com.example.finema.R
+
+private const val PICTURE_MEASURES = 500
+private const val FLAG_ZERO = 0
+
+fun ImageView.downloadAndSetImageUrl(url: String?) {
+ Glide
+ .with(this)
+ .load(url)
+ .centerInside()
+ .override(PICTURE_MEASURES, PICTURE_MEASURES)
+ .placeholder(R.drawable.movies_24)
+ .into(this)
+}
+
+fun ImageView.downloadAndSetImageUri(uri: Uri?) {
+ Glide
+ .with(this)
+ .load(uri)
+ .centerInside()
+ .placeholder(R.drawable.movies_24)
+ .into(this)
+}
+
+fun View.hideKeyboard() {
+ val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.hideSoftInputFromWindow(windowToken, FLAG_ZERO)
+}
diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml
new file mode 100644
index 00000000..2f7ef940
--- /dev/null
+++ b/app/src/main/res/anim/slide_down.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml
new file mode 100644
index 00000000..b5caae9c
--- /dev/null
+++ b/app/src/main/res/anim/slide_up.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..2b068d11
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/parasite1.jpg b/app/src/main/res/drawable-v24/parasite1.jpg
new file mode 100644
index 00000000..28f129c8
Binary files /dev/null and b/app/src/main/res/drawable-v24/parasite1.jpg differ
diff --git a/app/src/main/res/drawable/background_selector.xml b/app/src/main/res/drawable/background_selector.xml
new file mode 100644
index 00000000..6aa2e9f5
--- /dev/null
+++ b/app/src/main/res/drawable/background_selector.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bookmark_24.xml b/app/src/main/res/drawable/bookmark_24.xml
new file mode 100644
index 00000000..7e480e24
--- /dev/null
+++ b/app/src/main/res/drawable/bookmark_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/bookmark_border_24.xml b/app/src/main/res/drawable/bookmark_border_24.xml
new file mode 100644
index 00000000..61dc4cf3
--- /dev/null
+++ b/app/src/main/res/drawable/bookmark_border_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/corner_button.xml b/app/src/main/res/drawable/corner_button.xml
new file mode 100644
index 00000000..e44af957
--- /dev/null
+++ b/app/src/main/res/drawable/corner_button.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/custom_button.xml b/app/src/main/res/drawable/custom_button.xml
new file mode 100644
index 00000000..75cd53c3
--- /dev/null
+++ b/app/src/main/res/drawable/custom_button.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/default_profile_avatar.png b/app/src/main/res/drawable/default_profile_avatar.png
new file mode 100644
index 00000000..269eab83
Binary files /dev/null and b/app/src/main/res/drawable/default_profile_avatar.png differ
diff --git a/app/src/main/res/drawable/exit_to_app_24.xml b/app/src/main/res/drawable/exit_to_app_24.xml
new file mode 100644
index 00000000..83cdf05a
--- /dev/null
+++ b/app/src/main/res/drawable/exit_to_app_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher2_background.xml b/app/src/main/res/drawable/ic_launcher2_background.xml
new file mode 100644
index 00000000..ca3826a4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher2_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..07d5da9c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/info_24.xml b/app/src/main/res/drawable/info_24.xml
new file mode 100644
index 00000000..17255b7a
--- /dev/null
+++ b/app/src/main/res/drawable/info_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/movies_24.xml b/app/src/main/res/drawable/movies_24.xml
new file mode 100644
index 00000000..afcfb9c8
--- /dev/null
+++ b/app/src/main/res/drawable/movies_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/notifications_24.xml b/app/src/main/res/drawable/notifications_24.xml
new file mode 100644
index 00000000..21cb88d1
--- /dev/null
+++ b/app/src/main/res/drawable/notifications_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/outline_menu_24.xml b/app/src/main/res/drawable/outline_menu_24.xml
new file mode 100644
index 00000000..4350ba96
--- /dev/null
+++ b/app/src/main/res/drawable/outline_menu_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/outline_star_24.xml b/app/src/main/res/drawable/outline_star_24.xml
new file mode 100644
index 00000000..5412e798
--- /dev/null
+++ b/app/src/main/res/drawable/outline_star_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/restore_from_trash_24.xml b/app/src/main/res/drawable/restore_from_trash_24.xml
new file mode 100644
index 00000000..c83d9698
--- /dev/null
+++ b/app/src/main/res/drawable/restore_from_trash_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/search_24.xml b/app/src/main/res/drawable/search_24.xml
new file mode 100644
index 00000000..c0d516d4
--- /dev/null
+++ b/app/src/main/res/drawable/search_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/settings_24.xml b/app/src/main/res/drawable/settings_24.xml
new file mode 100644
index 00000000..41a82ede
--- /dev/null
+++ b/app/src/main/res/drawable/settings_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..3b807cfe
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/category_item.xml b/app/src/main/res/layout/category_item.xml
new file mode 100644
index 00000000..531b324a
--- /dev/null
+++ b/app/src/main/res/layout/category_item.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/choose_favourite_fragment.xml b/app/src/main/res/layout/choose_favourite_fragment.xml
new file mode 100644
index 00000000..894ab1bc
--- /dev/null
+++ b/app/src/main/res/layout/choose_favourite_fragment.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/favourite_fragment.xml b/app/src/main/res/layout/favourite_fragment.xml
new file mode 100644
index 00000000..f75b76d9
--- /dev/null
+++ b/app/src/main/res/layout/favourite_fragment.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/favourite_movie_item.xml b/app/src/main/res/layout/favourite_movie_item.xml
new file mode 100644
index 00000000..f6c46b40
--- /dev/null
+++ b/app/src/main/res/layout/favourite_movie_item.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_base.xml b/app/src/main/res/layout/fragment_base.xml
new file mode 100644
index 00000000..586012e5
--- /dev/null
+++ b/app/src/main/res/layout/fragment_base.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_category_tournament.xml b/app/src/main/res/layout/fragment_category_tournament.xml
new file mode 100644
index 00000000..30716179
--- /dev/null
+++ b/app/src/main/res/layout/fragment_category_tournament.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tournament.xml b/app/src/main/res/layout/fragment_tournament.xml
new file mode 100644
index 00000000..465d8115
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tournament.xml
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tournament_genres.xml b/app/src/main/res/layout/fragment_tournament_genres.xml
new file mode 100644
index 00000000..ba140451
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tournament_genres.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/genre_item.xml b/app/src/main/res/layout/genre_item.xml
new file mode 100644
index 00000000..3800d4d0
--- /dev/null
+++ b/app/src/main/res/layout/genre_item.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/higher_lower_fragment.xml b/app/src/main/res/layout/higher_lower_fragment.xml
new file mode 100644
index 00000000..9ed44c28
--- /dev/null
+++ b/app/src/main/res/layout/higher_lower_fragment.xml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/higher_lower_rating_fragment.xml b/app/src/main/res/layout/higher_lower_rating_fragment.xml
new file mode 100644
index 00000000..54ecca0b
--- /dev/null
+++ b/app/src/main/res/layout/higher_lower_rating_fragment.xml
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/movie_description.xml b/app/src/main/res/layout/movie_description.xml
new file mode 100644
index 00000000..00beee08
--- /dev/null
+++ b/app/src/main/res/layout/movie_description.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/movie_details_fragment.xml b/app/src/main/res/layout/movie_details_fragment.xml
new file mode 100644
index 00000000..89e29668
--- /dev/null
+++ b/app/src/main/res/layout/movie_details_fragment.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/movie_item.xml b/app/src/main/res/layout/movie_item.xml
new file mode 100644
index 00000000..cd62a9a9
--- /dev/null
+++ b/app/src/main/res/layout/movie_item.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml
new file mode 100644
index 00000000..570128b9
--- /dev/null
+++ b/app/src/main/res/layout/nav_header.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nice.xml b/app/src/main/res/layout/nice.xml
new file mode 100644
index 00000000..111bd1c5
--- /dev/null
+++ b/app/src/main/res/layout/nice.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/number_fragment.xml b/app/src/main/res/layout/number_fragment.xml
new file mode 100644
index 00000000..af2fdfe0
--- /dev/null
+++ b/app/src/main/res/layout/number_fragment.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/others_fragment.xml b/app/src/main/res/layout/others_fragment.xml
new file mode 100644
index 00000000..dca4e595
--- /dev/null
+++ b/app/src/main/res/layout/others_fragment.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/own_tournament_fragment.xml b/app/src/main/res/layout/own_tournament_fragment.xml
new file mode 100644
index 00000000..dca4e595
--- /dev/null
+++ b/app/src/main/res/layout/own_tournament_fragment.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/profile_fragment.xml b/app/src/main/res/layout/profile_fragment.xml
new file mode 100644
index 00000000..0a724bd6
--- /dev/null
+++ b/app/src/main/res/layout/profile_fragment.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml
new file mode 100644
index 00000000..cb8e31e2
--- /dev/null
+++ b/app/src/main/res/layout/settings_fragment.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sign_in_fragment.xml b/app/src/main/res/layout/sign_in_fragment.xml
new file mode 100644
index 00000000..e30052b2
--- /dev/null
+++ b/app/src/main/res/layout/sign_in_fragment.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/thematic_fragment.xml b/app/src/main/res/layout/thematic_fragment.xml
new file mode 100644
index 00000000..dca4e595
--- /dev/null
+++ b/app/src/main/res/layout/thematic_fragment.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tmp_fragment.xml b/app/src/main/res/layout/tmp_fragment.xml
new file mode 100644
index 00000000..41dde539
--- /dev/null
+++ b/app/src/main/res/layout/tmp_fragment.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/top_movie_item.xml b/app/src/main/res/layout/top_movie_item.xml
new file mode 100644
index 00000000..271306ce
--- /dev/null
+++ b/app/src/main/res/layout/top_movie_item.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/welcome_fragment.xml b/app/src/main/res/layout/welcome_fragment.xml
new file mode 100644
index 00000000..ead23849
--- /dev/null
+++ b/app/src/main/res/layout/welcome_fragment.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml
new file mode 100755
index 00000000..8d27b8ab
--- /dev/null
+++ b/app/src/main/res/menu/drawer_menu.xml
@@ -0,0 +1,49 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher2.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher2.xml
new file mode 100644
index 00000000..a1809a08
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher2.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher2_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher2_round.xml
new file mode 100644
index 00000000..a1809a08
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher2_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..a571e600
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher2.png b/app/src/main/res/mipmap-hdpi/ic_launcher2.png
new file mode 100644
index 00000000..c6beb9bf
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher2.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher2_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher2_foreground.png
new file mode 100644
index 00000000..c71712fe
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher2_foreground.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher2_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher2_round.png
new file mode 100644
index 00000000..31c96f11
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher2_round.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..61da551c
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c41dd285
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher2.png b/app/src/main/res/mipmap-mdpi/ic_launcher2.png
new file mode 100644
index 00000000..ee364f04
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher2.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher2_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher2_foreground.png
new file mode 100644
index 00000000..92f619d4
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher2_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher2_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher2_round.png
new file mode 100644
index 00000000..0fc893b8
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher2_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..db5080a7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..6dba46da
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher2.png b/app/src/main/res/mipmap-xhdpi/ic_launcher2.png
new file mode 100644
index 00000000..0c3d6f0a
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher2.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher2_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher2_foreground.png
new file mode 100644
index 00000000..3dc0d7aa
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher2_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher2_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher2_round.png
new file mode 100644
index 00000000..4403ee31
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher2_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..da31a871
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..15ac6817
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png
new file mode 100644
index 00000000..d997dedb
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher2_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher2_foreground.png
new file mode 100644
index 00000000..d3598397
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher2_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher2_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher2_round.png
new file mode 100644
index 00000000..ee1331e0
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher2_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..b216f2d3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..f25a4197
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png
new file mode 100644
index 00000000..686f5754
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_foreground.png
new file mode 100644
index 00000000..21d352a8
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_round.png
new file mode 100644
index 00000000..c169356d
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher2_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e96783cc
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/navigation/app_navigation_graph.xml b/app/src/main/res/navigation/app_navigation_graph.xml
new file mode 100644
index 00000000..630e23e8
--- /dev/null
+++ b/app/src/main/res/navigation/app_navigation_graph.xml
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..831a6632
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..915f6fa0
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,20 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #1D1D1D
+ #2D2D2D
+
+ #8561c5
+ #673ab7
+ #482880
+
+ #837E7E
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..f0c46f7b
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,10 @@
+
+
+ 16dp
+ 8dp
+ 10dp
+ 10dp
+
+ 16dp
+ 16dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..9f913816
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,46 @@
+
+ Finema
+ Профиль
+ О фильме
+ Жанры:
+ Рейтинг
+ Компании
+ Уведомления
+ Hello blank fragment
+ 16 Лучших фильмов
+ Раунд №1
+ Фильм 2
+ Фильм 1
+ Жанр
+ Очки: %1$d
+ https://image.tmdb.org/t/p/w342%1$s
+ Выйти из приложения
+ Очистить статистику
+ Выбери лучший фильм
+ Избранные фильмы
+ Настройки
+ Поиск фильмов
+ Игра больше/меньше
+ Популярность
+ %1$d Лучших фильмов
+ Раунд %1$d
+ Ваша статистика очищена
+ Level 1
+ Next Level
+ Здесь пусто\nДавайте найдем\nВаши любимые фильмы
+ Твой топ
+ Поиск
+ Гость
+ 8 800 555 35 35
+ Добавлено в избранные
+ Удалено из избранных
+ Войти как Гость
+ Авторизация через Google
+ Очки: %1$d
+ Раунд %1$d
+ Какой фильм сейчас популярнее?
+ Очки: 0
+ У какого фильма выше рейтинг?
+ Трейлер
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..158a38e0
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..4ef3d797
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml
new file mode 100644
index 00000000..277afa9d
--- /dev/null
+++ b/app/src/main/res/xml/settings.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/com/example/finema/ExampleUnitTest.kt b/app/src/test/java/com/example/finema/ExampleUnitTest.kt
new file mode 100644
index 00000000..1e7896cb
--- /dev/null
+++ b/app/src/test/java/com/example/finema/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.example.finema
+
+import kotlin.test.assertEquals
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..4f57cee0
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,31 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.32"
+ repositories {
+ mavenCentral()
+ google()
+ // jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.3'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
+ classpath 'com.google.gms:google-services:4.3.8'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ mavenCentral()
+ google()
+ // jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..98bed167
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..e09a1465
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon May 10 00:20:52 MSK 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..cccdd3d5
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..f9553162
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..182cf1a7
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Finema"
\ No newline at end of file