diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..fb7f4a8
--- /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 0000000..526b4c2
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f8b3a95
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..441a449
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,108 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+ // Kotlin Kapt
+ id 'kotlin-kapt'
+ // Parcelable
+ id("kotlin-parcelize")
+ // Hilt
+ id("dagger.hilt.android.plugin")
+ // Navigation Safe Args
+ id("androidx.navigation.safeargs.kotlin")
+}
+
+android {
+ compileSdk 31
+
+ defaultConfig {
+ applicationId "com.example.rickandmortyapp"
+ minSdk 21
+ targetSdk 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ 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'
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+
+ // Kotlin
+ // | Stdlib
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+
+ // Kotlin Coroutines
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2")
+
+ // Core
+ implementation 'androidx.core:core-ktx:1.7.0'
+
+ // Appcompat
+ implementation 'androidx.appcompat:appcompat:1.3.0'
+
+ // Material Design Components
+ implementation 'com.google.android.material:material:1.4.0'
+
+ // UI Components
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
+
+ // Hilt
+ implementation("com.google.dagger:hilt-android:$hilt_version")
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt("com.google.dagger:hilt-android-compiler:$hilt_version")
+
+ // Navigation Components
+ def nav_version = "2.3.5"
+ implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
+ implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
+
+ // Retrofit 2
+ def retrofit_version = "2.9.0"
+ implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
+ // Gson
+ implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
+
+ // OkHttp
+ implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.0"))
+
+ // define any required OkHttp artifacts without version
+ implementation("com.squareup.okhttp3:okhttp")
+ implementation("com.squareup.okhttp3:logging-interceptor")
+
+ // Activity version
+ def activity_version = "1.3.0"
+ implementation("androidx.activity:activity-ktx:$activity_version")
+
+ // Fragment version
+ def fragment_version = "1.3.6"
+ implementation("androidx.fragment:fragment-ktx:$fragment_version")
+
+ // ViewBindingPropertyDelegate
+ def view_binding_property_delegate = '1.5.3'
+ // To use only without reflection variants of viewBinding
+ implementation "com.github.kirich1409:viewbindingpropertydelegate-noreflection:$view_binding_property_delegate"
+
+ // Glide
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+
+ // Paging 3
+ def paging_version = "3.0.1"
+ implementation("androidx.paging:paging-runtime-ktx:$paging_version")
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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/rickandmortyapp/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/rickandmortyapp/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..1b64d2a
--- /dev/null
+++ b/app/src/androidTest/java/com/example/rickandmortyapp/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.rickandmortyapp
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * 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.rickandmortyapp", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0582726
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/App.kt b/app/src/main/java/com/example/rickandmortyapp/App.kt
new file mode 100644
index 0000000..7ab09e0
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/App.kt
@@ -0,0 +1,7 @@
+package com.example.rickandmortyapp
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class App : Application()
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BaseDiffUtilItemCallback.kt b/app/src/main/java/com/example/rickandmortyapp/base/BaseDiffUtilItemCallback.kt
new file mode 100644
index 0000000..6645e6e
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BaseDiffUtilItemCallback.kt
@@ -0,0 +1,14 @@
+package com.example.rickandmortyapp.base
+
+import androidx.recyclerview.widget.DiffUtil
+
+class BaseDiffUtilItemCallback : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
+ return oldItem == newItem
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BaseFragment.kt b/app/src/main/java/com/example/rickandmortyapp/base/BaseFragment.kt
new file mode 100644
index 0000000..d8b3196
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BaseFragment.kt
@@ -0,0 +1,39 @@
+package com.example.rickandmortyapp.base
+
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.LayoutRes
+import androidx.fragment.app.Fragment
+import androidx.viewbinding.ViewBinding
+
+abstract class BaseFragment(
+ @LayoutRes layoutId: Int
+) : Fragment(layoutId) {
+
+ protected abstract val binding: BaseViewBinding
+ protected abstract val viewModel: ViewModel
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initialize()
+ setupViews()
+ setupListeners()
+ setupRequests()
+ setupObserves()
+ }
+
+ open fun initialize() {
+ }
+
+ open fun setupViews() {
+ }
+
+ open fun setupListeners() {
+ }
+
+ open fun setupRequests() {
+ }
+
+ open fun setupObserves() {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BasePagingSource.kt b/app/src/main/java/com/example/rickandmortyapp/base/BasePagingSource.kt
new file mode 100644
index 0000000..0d0dbdc
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BasePagingSource.kt
@@ -0,0 +1,45 @@
+package com.example.rickandmortyapp.base
+
+import android.net.Uri
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import com.bumptech.glide.load.HttpException
+import com.example.rickandmortyapp.data.network.dtos.models.ApiResponse
+import java.io.IOException
+
+abstract class BasePagingSource(
+ private val service: suspend (nextPage: Int) -> ApiResponse,
+ private val mappedData: (data: List) -> List
+) : PagingSource() {
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ return try {
+ val nextPage = params.key ?: 1
+ val response = service(nextPage)
+ val next = response.info.next
+ val nextPageNumber = if (next == null) {
+ null
+ } else {
+ Uri.parse(next).getQueryParameter("page")?.toInt()
+ }
+ LoadResult.Page(
+ data = mappedData(response.results),
+ prevKey = null,
+ nextKey = nextPageNumber
+ )
+ } catch (http: HttpException) {
+ LoadResult.Error(http)
+ } catch (e: IOException) {
+ LoadResult.Error(e)
+ } catch (exception: Exception) {
+ LoadResult.Error(exception)
+ }
+ }
+
+ override fun getRefreshKey(state: PagingState): Int? {
+ return state.anchorPosition?.let { anchorPosition ->
+ val anchorPage = state.closestPageToPosition(anchorPosition)
+ anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BaseRecyclerViewHolder.kt b/app/src/main/java/com/example/rickandmortyapp/base/BaseRecyclerViewHolder.kt
new file mode 100644
index 0000000..e236f74
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BaseRecyclerViewHolder.kt
@@ -0,0 +1,11 @@
+package com.example.rickandmortyapp.base
+
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+
+abstract class BaseRecyclerViewHolder(
+ val binding: V
+) : RecyclerView.ViewHolder(binding.root) {
+
+ abstract fun onBind(item: I): Unit?
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BaseRepository.kt b/app/src/main/java/com/example/rickandmortyapp/base/BaseRepository.kt
new file mode 100644
index 0000000..4c2e1c0
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BaseRepository.kt
@@ -0,0 +1,46 @@
+package com.example.rickandmortyapp.base
+
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import com.example.rickandmortyapp.common.resource.Resource
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+
+abstract class BaseRepository {
+
+ protected fun doRequest(request: suspend () -> T) = flow {
+ emit(Resource.Loading())
+ try {
+ emit(Resource.Success(data = request()))
+ } catch (e: Exception) {
+ emit(
+ Resource.Error(
+ data = null, message = e.localizedMessage ?: "Error Occurred"
+ )
+ )
+ }
+ }
+
+ protected fun doPagingRequest(
+ pagingSource: BasePagingSource,
+ pageSize: Int = 10,
+ prefetchDistance: Int = pageSize,
+ enabledPlaceholder: Boolean = true,
+ initialLoadSize: Int = pageSize * 3,
+ maxSize: Int = Int.MAX_VALUE,
+ jumpThreshold: Int = Int.MIN_VALUE,
+ ): Flow> {
+ return Pager(
+ config = PagingConfig(
+ pageSize,
+ prefetchDistance,
+ enabledPlaceholder,
+ initialLoadSize,
+ maxSize,
+ jumpThreshold
+ ),
+ pagingSourceFactory = { pagingSource }
+ ).flow
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/BaseViewModel.kt b/app/src/main/java/com/example/rickandmortyapp/base/BaseViewModel.kt
new file mode 100644
index 0000000..c630f68
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/BaseViewModel.kt
@@ -0,0 +1,38 @@
+package com.example.rickandmortyapp.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.rickandmortyapp.common.resource.Resource
+import com.example.rickandmortyapp.presentation.state.UIState
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+abstract class BaseViewModel : ViewModel() {
+
+ protected fun subscribeTo(
+ state: MutableLiveData>,
+ request: () -> Flow>
+ ) {
+ viewModelScope.launch {
+ request().collect {
+ when (it) {
+ is Resource.Loading -> {
+ state.value = UIState.Loading()
+ }
+ is Resource.Error -> {
+ it.message?.let { error ->
+ state.value = UIState.Error(error)
+ }
+ }
+ is Resource.Success -> {
+ it.data?.let { data ->
+ state.value = UIState.Success(data)
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/base/IBaseDiffModel.kt b/app/src/main/java/com/example/rickandmortyapp/base/IBaseDiffModel.kt
new file mode 100644
index 0000000..339ba9a
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/base/IBaseDiffModel.kt
@@ -0,0 +1,6 @@
+package com.example.rickandmortyapp.base
+
+interface IBaseDiffModel {
+ val id: Int?
+ override fun equals(other: Any?): Boolean
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/constants/Constants.kt b/app/src/main/java/com/example/rickandmortyapp/common/constants/Constants.kt
new file mode 100644
index 0000000..92e7d6a
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/constants/Constants.kt
@@ -0,0 +1,9 @@
+package com.example.rickandmortyapp.common.constants
+
+object Constants {
+
+ const val BASE_URL = "https://rickandmortyapi.com/"
+ const val CHARACTER_VIEW_TYPE = 0
+ const val LOCATION_VIEW_TYPE = 1
+ const val EPISODES_VIEW_TYPE = 2
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/DateFormatExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/DateFormatExtension.kt
new file mode 100644
index 0000000..8f84390
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/DateFormatExtension.kt
@@ -0,0 +1,22 @@
+package com.example.rickandmortyapp.common.extensions
+
+import android.annotation.SuppressLint
+import android.icu.text.SimpleDateFormat
+import java.text.ParseException
+import java.util.*
+
+@SuppressLint("SimpleDateFormat", "NewApi")
+fun toFormatDate(oldStringDate: String?): String? {
+ if (oldStringDate == null || oldStringDate == "")
+ return ""
+ val newDate: String?
+ val dateFormat = SimpleDateFormat("MMMM d yyyy - mm:ss", Locale.ENGLISH)
+ newDate = try {
+ val date: Date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(oldStringDate)
+ dateFormat.format(date)
+ } catch (e: ParseException) {
+ e.printStackTrace()
+ oldStringDate
+ }
+ return newDate
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/GlideExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/GlideExtension.kt
new file mode 100644
index 0000000..2e7e48c
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/GlideExtension.kt
@@ -0,0 +1,12 @@
+package com.example.rickandmortyapp.common.extensions
+
+import android.widget.ImageView
+import com.bumptech.glide.Glide
+
+fun ImageView.loadImage(url: String?, placeHolder: Int = 0) {
+ Glide.with(this.context)
+ .load(url)
+ .error(placeHolder)
+ .into(this)
+}
+
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/KeyboardExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/KeyboardExtension.kt
new file mode 100644
index 0000000..98c7ce3
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/KeyboardExtension.kt
@@ -0,0 +1,33 @@
+package com.example.rickandmortyapp.common.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import androidx.fragment.app.Fragment
+
+fun Activity.hideKeyboard() {
+ hideKeyboard(currentFocus ?: View(this))
+}
+
+fun Fragment.hideKeyboard() {
+ view?.let { activity?.hideKeyboard(it) }
+}
+
+fun Activity.showKeyboard() {
+ showKeyboard(currentFocus ?: View(this))
+}
+
+fun Fragment.showKeyboard() {
+ view?.let { activity?.showKeyboard(it) }
+}
+
+fun Context.hideKeyboard(view: View) {
+ val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
+}
+
+fun Context.showKeyboard(view: View) {
+ val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/LiveDataExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/LiveDataExtension.kt
new file mode 100644
index 0000000..6b53cd5
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/LiveDataExtension.kt
@@ -0,0 +1,27 @@
+package com.example.rickandmortyapp.common.extensions
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+
+fun zipLiveData(a: LiveData, b: LiveData): LiveData> {
+ return MediatorLiveData>().apply {
+ var lastA: A? = null
+ var lastB: B? = null
+
+ fun update() {
+ val localLastA = lastA
+ val localLastB = lastB
+ if (localLastA != null && localLastB != null)
+ this.value = Pair(localLastA, localLastB)
+ }
+
+ addSource(a) {
+ lastA = it
+ update()
+ }
+ addSource(b) {
+ lastB = it
+ update()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/SearchViewExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/SearchViewExtension.kt
new file mode 100644
index 0000000..665d901
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/SearchViewExtension.kt
@@ -0,0 +1,42 @@
+package com.example.rickandmortyapp.common.extensions
+
+import android.content.Context
+import android.graphics.Color
+import android.view.MenuItem
+import android.widget.SearchView
+import com.example.rickandmortyapp.R
+import kotlinx.coroutines.Job
+
+fun SearchView.submitSearch(getFilterByName: (query: String?) -> Job) {
+ this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ getFilterByName(query)
+ return false
+ }
+
+ override fun onQueryTextChange(query: String?): Boolean {
+ getFilterByName(query)
+ return false
+ }
+ })
+}
+
+fun SearchView.setTools(context: Context?) {
+ this.queryHint = context?.getString(R.string.searching)
+ this.setBackgroundColor(Color.parseColor("#0C0D0E"))
+}
+
+fun MenuItem.setOnActionExpandListener(searchView: SearchView, hideKeyboard: () -> Unit) {
+ this.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
+ override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
+ searchView.isIconified = false
+ searchView.onActionViewExpanded()
+ return true
+ }
+
+ override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
+ hideKeyboard()
+ return true
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/extensions/SingleClickListenerExtension.kt b/app/src/main/java/com/example/rickandmortyapp/common/extensions/SingleClickListenerExtension.kt
new file mode 100644
index 0000000..9092497
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/extensions/SingleClickListenerExtension.kt
@@ -0,0 +1,17 @@
+package com.example.rickandmortyapp.common.extensions
+
+import android.view.View
+import android.widget.SearchView
+import com.example.rickandmortyapp.common.singlelistener.OnSingleClickListener
+
+fun SearchView.setOnSingleClickListener(l: View.OnFocusChangeListener) {
+ setOnQueryTextFocusChangeListener(OnSingleClickListener(l))
+}
+
+fun SearchView.setOnSingleClickListener(l: (View) -> Unit) {
+ setOnQueryTextFocusChangeListener(OnSingleClickListener(l))
+}
+
+fun View.setOnSingleClickListener(l: (View) -> Unit) {
+ setOnClickListener(OnSingleClickListener(l))
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/resource/Resource.kt b/app/src/main/java/com/example/rickandmortyapp/common/resource/Resource.kt
new file mode 100644
index 0000000..d436a8d
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/resource/Resource.kt
@@ -0,0 +1,10 @@
+package com.example.rickandmortyapp.common.resource
+
+sealed class Resource(
+ val data: T? = null,
+ val message: String? = null,
+) {
+ class Loading(data: T? = null) : Resource(data = data)
+ class Success(data: T) : Resource(data = data)
+ class Error(message: String, data: T? = null) : Resource(data = data, message = message)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/common/singlelistener/OnSingleClickListener.kt b/app/src/main/java/com/example/rickandmortyapp/common/singlelistener/OnSingleClickListener.kt
new file mode 100644
index 0000000..281b599
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/common/singlelistener/OnSingleClickListener.kt
@@ -0,0 +1,48 @@
+package com.example.rickandmortyapp.common.singlelistener
+
+import android.view.View
+
+class OnSingleClickListener : View.OnFocusChangeListener, View.OnClickListener {
+
+ private lateinit var onClickListener: View.OnClickListener
+
+ private var onFocusChangeListener: View.OnFocusChangeListener
+
+ constructor(listener: View.OnFocusChangeListener) {
+ onFocusChangeListener = listener
+ }
+
+ constructor(listener: (View) -> Unit) {
+ onFocusChangeListener = View.OnFocusChangeListener { it, hasFocus ->
+ if (hasFocus) {
+ listener.invoke(it)
+ }
+ }
+ onClickListener = View.OnClickListener { listener.invoke(it) }
+ }
+
+
+ override fun onFocusChange(v: View?, h: Boolean) {
+ val currentTimeMillis = System.currentTimeMillis()
+
+ if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
+ previousClickTimeMillis = currentTimeMillis
+ onFocusChangeListener.onFocusChange(v, h)
+ }
+ }
+
+ override fun onClick(v: View?) {
+ val currentTimeMillis = System.currentTimeMillis()
+
+ if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
+ previousClickTimeMillis = currentTimeMillis
+ onClickListener.onClick(v)
+ }
+ }
+
+ companion object {
+ private const val DELAY_MILLIS = 200L
+ private var previousClickTimeMillis = 0L
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/CharactersRepositoryImpl.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/CharactersRepositoryImpl.kt
new file mode 100644
index 0000000..59f43db
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/CharactersRepositoryImpl.kt
@@ -0,0 +1,15 @@
+package com.example.rickandmortyapp.data.implementation
+
+import com.example.rickandmortyapp.base.BaseRepository
+import com.example.rickandmortyapp.data.implementation.pagingsources.CharactersPagingSource
+import com.example.rickandmortyapp.data.network.apiservices.CharactersApiService
+import com.example.rickandmortyapp.domain.repositories.CharactersRepository
+import javax.inject.Inject
+
+class CharactersRepositoryImpl @Inject constructor(
+ private val service: CharactersApiService
+) : BaseRepository(), CharactersRepository {
+
+ override fun getCharacters(name: String) =
+ doPagingRequest(CharactersPagingSource(service, name))
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/EpisodesRepositoryImpl.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/EpisodesRepositoryImpl.kt
new file mode 100644
index 0000000..3339e22
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/EpisodesRepositoryImpl.kt
@@ -0,0 +1,14 @@
+package com.example.rickandmortyapp.data.implementation
+
+import com.example.rickandmortyapp.base.BaseRepository
+import com.example.rickandmortyapp.data.implementation.pagingsources.EpisodesPagingSource
+import com.example.rickandmortyapp.data.network.apiservices.EpisodesApiService
+import com.example.rickandmortyapp.domain.repositories.EpisodesRepository
+import javax.inject.Inject
+
+class EpisodesRepositoryImpl @Inject constructor(
+ private val service: EpisodesApiService
+) : BaseRepository(), EpisodesRepository {
+
+ override fun getEpisodes(name: String) = doPagingRequest(EpisodesPagingSource(service, name))
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/LocationsRepositoryImpl.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/LocationsRepositoryImpl.kt
new file mode 100644
index 0000000..e15061d
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/LocationsRepositoryImpl.kt
@@ -0,0 +1,14 @@
+package com.example.rickandmortyapp.data.implementation
+
+import com.example.rickandmortyapp.base.BaseRepository
+import com.example.rickandmortyapp.data.implementation.pagingsources.LocationsPagingSource
+import com.example.rickandmortyapp.data.network.apiservices.LocationsApiService
+import com.example.rickandmortyapp.domain.repositories.LocationsRepository
+import javax.inject.Inject
+
+class LocationsRepositoryImpl @Inject constructor(
+ private val service: LocationsApiService
+) : BaseRepository(), LocationsRepository {
+
+ override fun getLocations(name: String) = doPagingRequest(LocationsPagingSource(service, name))
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/CharactersPagingSource.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/CharactersPagingSource.kt
new file mode 100644
index 0000000..9ac32b6
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/CharactersPagingSource.kt
@@ -0,0 +1,15 @@
+package com.example.rickandmortyapp.data.implementation.pagingsources
+
+import com.example.rickandmortyapp.base.BasePagingSource
+import com.example.rickandmortyapp.data.network.apiservices.CharactersApiService
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import com.example.rickandmortyapp.data.network.dtos.models.toCharacters
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class CharactersPagingSource(
+ private val service: CharactersApiService,
+ private val name: String
+) : BasePagingSource(
+ { service.getCharacters(it, name) },
+ { data -> data.map { it.toCharacters() } }
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/EpisodesPagingSource.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/EpisodesPagingSource.kt
new file mode 100644
index 0000000..e9194b3
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/EpisodesPagingSource.kt
@@ -0,0 +1,16 @@
+package com.example.rickandmortyapp.data.implementation.pagingsources
+
+import com.example.rickandmortyapp.base.BasePagingSource
+import com.example.rickandmortyapp.data.network.apiservices.EpisodesApiService
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import com.example.rickandmortyapp.data.network.dtos.models.toEpisodes
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class EpisodesPagingSource(
+ private val service: EpisodesApiService,
+ private val name: String
+) :
+ BasePagingSource(
+ { service.getEpisodes(it, name) },
+ { data -> data.map { it.toEpisodes() } }
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/LocationsPagingSource.kt b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/LocationsPagingSource.kt
new file mode 100644
index 0000000..f77d7ee
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/implementation/pagingsources/LocationsPagingSource.kt
@@ -0,0 +1,16 @@
+package com.example.rickandmortyapp.data.implementation.pagingsources
+
+import com.example.rickandmortyapp.base.BasePagingSource
+import com.example.rickandmortyapp.data.network.apiservices.LocationsApiService
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import com.example.rickandmortyapp.data.network.dtos.models.toLocations
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class LocationsPagingSource(
+ private val service: LocationsApiService,
+ private val name: String
+) :
+ BasePagingSource(
+ { service.getLocations(it, name) },
+ { data -> data.map { it.toLocations() } }
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/CharactersApiService.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/CharactersApiService.kt
new file mode 100644
index 0000000..7bfbcb9
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/CharactersApiService.kt
@@ -0,0 +1,15 @@
+package com.example.rickandmortyapp.data.network.apiservices
+
+import com.example.rickandmortyapp.data.network.dtos.models.ApiResponse
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface CharactersApiService {
+
+ @GET("api/character")
+ suspend fun getCharacters(
+ @Query("page") page: Int,
+ @Query("name") name: String
+ ): ApiResponse
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/EpisodesApiService.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/EpisodesApiService.kt
new file mode 100644
index 0000000..295547f
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/EpisodesApiService.kt
@@ -0,0 +1,15 @@
+package com.example.rickandmortyapp.data.network.apiservices
+
+import com.example.rickandmortyapp.data.network.dtos.models.ApiResponse
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface EpisodesApiService {
+
+ @GET("api/episode")
+ suspend fun getEpisodes(
+ @Query("page") page: Int,
+ @Query("name") name: String
+ ): ApiResponse
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/LocationsApiService.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/LocationsApiService.kt
new file mode 100644
index 0000000..72855d3
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/apiservices/LocationsApiService.kt
@@ -0,0 +1,15 @@
+package com.example.rickandmortyapp.data.network.apiservices
+
+import com.example.rickandmortyapp.data.network.dtos.models.ApiResponse
+import com.example.rickandmortyapp.data.network.dtos.models.RickAndMortyDto
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface LocationsApiService {
+
+ @GET("api/location")
+ suspend fun getLocations(
+ @Query("page") page: Int,
+ @Query("name") name: String
+ ): ApiResponse
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/ApiResponse.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/ApiResponse.kt
new file mode 100644
index 0000000..2a93b6a
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/ApiResponse.kt
@@ -0,0 +1,12 @@
+package com.example.rickandmortyapp.data.network.dtos.models
+
+import com.google.gson.annotations.SerializedName
+
+data class ApiResponse(
+
+ @SerializedName("results")
+ val results: ArrayList,
+
+ @SerializedName("info")
+ val info: Info
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Info.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Info.kt
new file mode 100644
index 0000000..1ffaef4
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Info.kt
@@ -0,0 +1,18 @@
+package com.example.rickandmortyapp.data.network.dtos.models
+
+import com.google.gson.annotations.SerializedName
+
+data class Info(
+
+ @SerializedName("next")
+ val next: String?,
+
+ @SerializedName("pages")
+ val pages: Int?,
+
+ @SerializedName("prev")
+ val prev: String?,
+
+ @SerializedName("count")
+ val count: Int?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Mapper.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Mapper.kt
new file mode 100644
index 0000000..d4e7ef7
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/Mapper.kt
@@ -0,0 +1,95 @@
+package com.example.rickandmortyapp.data.network.dtos.models
+
+import com.example.rickandmortyapp.data.network.dtos.models.character.CharactersModelDto
+import com.example.rickandmortyapp.data.network.dtos.models.character.LocationDto
+import com.example.rickandmortyapp.data.network.dtos.models.character.OriginDto
+import com.example.rickandmortyapp.data.network.dtos.models.episode.EpisodesModelDto
+import com.example.rickandmortyapp.data.network.dtos.models.location.LocationsModelDto
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import com.example.rickandmortyapp.domain.models.character.CharactersModel
+import com.example.rickandmortyapp.domain.models.character.Location
+import com.example.rickandmortyapp.domain.models.character.Origin
+import com.example.rickandmortyapp.domain.models.episode.EpisodesModel
+import com.example.rickandmortyapp.domain.models.location.LocationsModel
+
+fun RickAndMortyDto.CharactersItem.toCharacters(): RickAndMorty.CharactersItem =
+ RickAndMorty.CharactersItem(
+ image,
+ gender,
+ species,
+ created,
+ origin.toOrigin(),
+ name,
+ location.toLocation(),
+ episode,
+ id,
+ type,
+ url,
+ status
+ )
+
+fun CharactersModelDto.toCharactersModel(): CharactersModel =
+ CharactersModel(
+ image,
+ gender,
+ species,
+ created,
+ origin.toOrigin(),
+ name,
+ location.toLocation(),
+ episode,
+ id,
+ type,
+ url,
+ status
+ )
+
+fun OriginDto.toOrigin(): Origin =
+ Origin(name, url)
+
+fun LocationDto.toLocation(): Location =
+ Location(name, url)
+
+fun RickAndMortyDto.LocationsItem.toLocations(): RickAndMorty.LocationsItem =
+ RickAndMorty.LocationsItem(
+ created,
+ name,
+ residents,
+ id,
+ type,
+ dimension,
+ url
+ )
+
+fun LocationsModelDto.toLocationsModel(): LocationsModel =
+ LocationsModel(
+ created,
+ name,
+ residents,
+ id,
+ type,
+ dimension,
+ url
+ )
+
+fun RickAndMortyDto.EpisodesItem.toEpisodes(): RickAndMorty.EpisodesItem =
+ RickAndMorty.EpisodesItem(
+ airDate,
+ characters,
+ created,
+ episode,
+ name,
+ id,
+ url
+ )
+
+fun EpisodesModelDto.toEpisodesModel(): EpisodesModel =
+ EpisodesModel(
+ airDate,
+ characters,
+ created,
+ episode,
+ name,
+ id,
+ url
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/RickAndMortyDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/RickAndMortyDto.kt
new file mode 100644
index 0000000..b81093f
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/RickAndMortyDto.kt
@@ -0,0 +1,95 @@
+package com.example.rickandmortyapp.data.network.dtos.models
+
+import com.example.rickandmortyapp.data.network.dtos.models.character.LocationDto
+import com.example.rickandmortyapp.data.network.dtos.models.character.OriginDto
+import com.google.gson.annotations.SerializedName
+
+sealed class RickAndMortyDto {
+
+ data class CharactersItem(
+
+ @SerializedName("image")
+ val image: String?,
+
+ @SerializedName("gender")
+ val gender: String?,
+
+ @SerializedName("species")
+ val species: String?,
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("origin")
+ val origin: OriginDto,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("location")
+ val location: LocationDto,
+
+ @SerializedName("episode")
+ val episode: List?,
+
+ @SerializedName("id")
+ val id: Int?,
+
+ @SerializedName("type")
+ val type: String?,
+
+ @SerializedName("url")
+ val url: String?,
+
+ @SerializedName("status")
+ val status: String?
+ ) : RickAndMortyDto()
+
+ data class EpisodesItem(
+
+ @SerializedName("air_date")
+ val airDate: String?,
+
+ @SerializedName("characters")
+ val characters: List?,
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("episode")
+ val episode: String?,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("id")
+ val id: Int?,
+
+ @SerializedName("url")
+ val url: String?
+ ) : RickAndMortyDto()
+
+ data class LocationsItem(
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("residents")
+ val residents: List?,
+
+ @SerializedName("id")
+ val id: Int?,
+
+ @SerializedName("type")
+ val type: String?,
+
+ @SerializedName("dimension")
+ val dimension: String?,
+
+ @SerializedName("url")
+ val url: String?
+ ) : RickAndMortyDto()
+}
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/CharactersModelDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/CharactersModelDto.kt
new file mode 100644
index 0000000..ff09ac9
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/CharactersModelDto.kt
@@ -0,0 +1,43 @@
+package com.example.rickandmortyapp.data.network.dtos.models.character
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+import com.google.gson.annotations.SerializedName
+
+data class CharactersModelDto(
+
+ @SerializedName("image")
+ val image: String?,
+
+ @SerializedName("gender")
+ val gender: String?,
+
+ @SerializedName("species")
+ val species: String?,
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("origin")
+ val origin: OriginDto,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("location")
+ val location: LocationDto,
+
+ @SerializedName("episode")
+ val episode: List?,
+
+ @SerializedName("id")
+ override val id: Int?,
+
+ @SerializedName("type")
+ val type: String?,
+
+ @SerializedName("url")
+ val url: String?,
+
+ @SerializedName("status")
+ val status: String?
+) : IBaseDiffModel
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/LocationDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/LocationDto.kt
new file mode 100644
index 0000000..2cf7b3d
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/LocationDto.kt
@@ -0,0 +1,6 @@
+package com.example.rickandmortyapp.data.network.dtos.models.character
+
+data class LocationDto(
+ val name: String?,
+ val url: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/OriginDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/OriginDto.kt
new file mode 100644
index 0000000..8e38a89
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/character/OriginDto.kt
@@ -0,0 +1,6 @@
+package com.example.rickandmortyapp.data.network.dtos.models.character
+
+data class OriginDto(
+ val name: String?,
+ val url: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/episode/EpisodesModelDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/episode/EpisodesModelDto.kt
new file mode 100644
index 0000000..04402fc
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/episode/EpisodesModelDto.kt
@@ -0,0 +1,28 @@
+package com.example.rickandmortyapp.data.network.dtos.models.episode
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+import com.google.gson.annotations.SerializedName
+
+data class EpisodesModelDto(
+
+ @SerializedName("air_date")
+ val airDate: String?,
+
+ @SerializedName("characters")
+ val characters: List?,
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("episode")
+ val episode: String?,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("id")
+ override val id: Int?,
+
+ @SerializedName("url")
+ val url: String?
+) : IBaseDiffModel
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/location/LocationsModelDto.kt b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/location/LocationsModelDto.kt
new file mode 100644
index 0000000..ee2005e
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/data/network/dtos/models/location/LocationsModelDto.kt
@@ -0,0 +1,28 @@
+package com.example.rickandmortyapp.data.network.dtos.models.location
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+import com.google.gson.annotations.SerializedName
+
+data class LocationsModelDto(
+
+ @SerializedName("created")
+ val created: String?,
+
+ @SerializedName("name")
+ val name: String?,
+
+ @SerializedName("residents")
+ val residents: List?,
+
+ @SerializedName("id")
+ override val id: Int?,
+
+ @SerializedName("type")
+ val type: String?,
+
+ @SerializedName("dimension")
+ val dimension: String?,
+
+ @SerializedName("url")
+ val url: String?
+) : IBaseDiffModel
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/di/NetworkModule.kt b/app/src/main/java/com/example/rickandmortyapp/di/NetworkModule.kt
new file mode 100644
index 0000000..953005a
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/di/NetworkModule.kt
@@ -0,0 +1,58 @@
+package com.example.rickandmortyapp.di
+
+import com.example.rickandmortyapp.common.constants.Constants
+import com.example.rickandmortyapp.data.network.apiservices.CharactersApiService
+import com.example.rickandmortyapp.data.network.apiservices.EpisodesApiService
+import com.example.rickandmortyapp.data.network.apiservices.LocationsApiService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object NetworkModule {
+
+ @Singleton
+ @Provides
+ fun createOkHttpClient() =
+ OkHttpClient().newBuilder()
+ .addInterceptor(
+ HttpLoggingInterceptor().setLevel(
+ HttpLoggingInterceptor.Level.BODY
+ )
+ )
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .build()
+
+ @Singleton
+ @Provides
+ fun createRetrofitClient(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
+ .baseUrl(Constants.BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .client(okHttpClient)
+ .build()
+
+ @Singleton
+ @Provides
+ fun provideCharactersApiService(retrofit: Retrofit): CharactersApiService =
+ retrofit.create(CharactersApiService::class.java)
+
+ @Singleton
+ @Provides
+ fun provideLocationsApiService(retrofit: Retrofit): LocationsApiService =
+ retrofit.create(LocationsApiService::class.java)
+
+ @Singleton
+ @Provides
+ fun provideEpisodesApiService(retrofit: Retrofit): EpisodesApiService =
+ retrofit.create(EpisodesApiService::class.java)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/di/RepositoriesModule.kt b/app/src/main/java/com/example/rickandmortyapp/di/RepositoriesModule.kt
new file mode 100644
index 0000000..599287d
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/di/RepositoriesModule.kt
@@ -0,0 +1,32 @@
+package com.example.rickandmortyapp.di
+
+import com.example.rickandmortyapp.data.implementation.CharactersRepositoryImpl
+import com.example.rickandmortyapp.data.implementation.EpisodesRepositoryImpl
+import com.example.rickandmortyapp.data.implementation.LocationsRepositoryImpl
+import com.example.rickandmortyapp.data.network.apiservices.CharactersApiService
+import com.example.rickandmortyapp.data.network.apiservices.EpisodesApiService
+import com.example.rickandmortyapp.data.network.apiservices.LocationsApiService
+import com.example.rickandmortyapp.domain.repositories.CharactersRepository
+import com.example.rickandmortyapp.domain.repositories.EpisodesRepository
+import com.example.rickandmortyapp.domain.repositories.LocationsRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+object RepositoriesModule {
+
+ @Provides
+ fun provideCharacterRepository(service: CharactersApiService): CharactersRepository =
+ CharactersRepositoryImpl(service)
+
+ @Provides
+ fun provideLocationsRepository(service: LocationsApiService): LocationsRepository =
+ LocationsRepositoryImpl(service)
+
+ @Provides
+ fun provideEpisodesRepository(service: EpisodesApiService): EpisodesRepository =
+ EpisodesRepositoryImpl(service)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/PagingData.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/PagingData.kt
new file mode 100644
index 0000000..515245a
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/PagingData.kt
@@ -0,0 +1,3 @@
+package com.example.rickandmortyapp.domain.models
+
+typealias PagingList = Any
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/RickAndMorty.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/RickAndMorty.kt
new file mode 100644
index 0000000..e392f80
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/RickAndMorty.kt
@@ -0,0 +1,42 @@
+package com.example.rickandmortyapp.domain.models
+
+import com.example.rickandmortyapp.domain.models.character.Location
+import com.example.rickandmortyapp.domain.models.character.Origin
+
+sealed class RickAndMorty {
+
+ data class CharactersItem(
+ val image: String?,
+ val gender: String?,
+ val species: String?,
+ val created: String?,
+ val origin: Origin,
+ val name: String?,
+ val location: Location,
+ val episode: List?,
+ val id: Int?,
+ val type: String?,
+ val url: String?,
+ val status: String?
+ ) : RickAndMorty()
+
+ data class EpisodesItem(
+ val airDate: String?,
+ val characters: List?,
+ val created: String?,
+ val episode: String?,
+ val name: String?,
+ val id: Int?,
+ val url: String?
+ ) : RickAndMorty()
+
+ data class LocationsItem(
+ val created: String?,
+ val name: String?,
+ val residents: List?,
+ val id: Int?,
+ val type: String?,
+ val dimension: String?,
+ val url: String?
+ ) : RickAndMorty()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/character/CharactersModel.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/CharactersModel.kt
new file mode 100644
index 0000000..839950e
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/CharactersModel.kt
@@ -0,0 +1,18 @@
+package com.example.rickandmortyapp.domain.models.character
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+
+data class CharactersModel(
+ val image: String?,
+ val gender: String?,
+ val species: String?,
+ val created: String?,
+ val origin: Origin,
+ val name: String?,
+ val location: Location,
+ val episode: List?,
+ override val id: Int?,
+ val type: String?,
+ val url: String?,
+ val status: String?
+) : IBaseDiffModel
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Location.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Location.kt
new file mode 100644
index 0000000..431e3b0
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Location.kt
@@ -0,0 +1,6 @@
+package com.example.rickandmortyapp.domain.models.character
+
+data class Location(
+ val name: String?,
+ val url: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Origin.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Origin.kt
new file mode 100644
index 0000000..8debbc8
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/character/Origin.kt
@@ -0,0 +1,6 @@
+package com.example.rickandmortyapp.domain.models.character
+
+data class Origin(
+ val name: String?,
+ val url: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/episode/EpisodesModel.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/episode/EpisodesModel.kt
new file mode 100644
index 0000000..e272c51
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/episode/EpisodesModel.kt
@@ -0,0 +1,13 @@
+package com.example.rickandmortyapp.domain.models.episode
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+
+data class EpisodesModel(
+ val airDate: String?,
+ val characters: List?,
+ val created: String?,
+ val episode: String?,
+ val name: String?,
+ override val id: Int?,
+ val url: String?
+) : IBaseDiffModel
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/models/location/LocationsModel.kt b/app/src/main/java/com/example/rickandmortyapp/domain/models/location/LocationsModel.kt
new file mode 100644
index 0000000..5ddacb8
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/models/location/LocationsModel.kt
@@ -0,0 +1,13 @@
+package com.example.rickandmortyapp.domain.models.location
+
+import com.example.rickandmortyapp.base.IBaseDiffModel
+
+data class LocationsModel(
+ val created: String?,
+ val name: String?,
+ val residents: List?,
+ override val id: Int?,
+ val type: String?,
+ val dimension: String?,
+ val url: String?
+) : IBaseDiffModel
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/repositories/CharactersRepository.kt b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/CharactersRepository.kt
new file mode 100644
index 0000000..8ab5ab5
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/CharactersRepository.kt
@@ -0,0 +1,10 @@
+package com.example.rickandmortyapp.domain.repositories
+
+import com.example.rickandmortyapp.domain.models.PagingList
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import kotlinx.coroutines.flow.Flow
+
+interface CharactersRepository {
+
+ fun getCharacters(name: String): Flow>
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/repositories/EpisodesRepository.kt b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/EpisodesRepository.kt
new file mode 100644
index 0000000..1e0412c
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/EpisodesRepository.kt
@@ -0,0 +1,10 @@
+package com.example.rickandmortyapp.domain.repositories
+
+import com.example.rickandmortyapp.domain.models.PagingList
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import kotlinx.coroutines.flow.Flow
+
+interface EpisodesRepository {
+
+ fun getEpisodes(name: String): Flow>
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/repositories/LocationsRepository.kt b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/LocationsRepository.kt
new file mode 100644
index 0000000..497656f
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/repositories/LocationsRepository.kt
@@ -0,0 +1,10 @@
+package com.example.rickandmortyapp.domain.repositories
+
+import com.example.rickandmortyapp.domain.models.PagingList
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import kotlinx.coroutines.flow.Flow
+
+interface LocationsRepository {
+
+ fun getLocations(name: String): Flow>
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetCharactersUseCase.kt b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetCharactersUseCase.kt
new file mode 100644
index 0000000..0bc05a9
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetCharactersUseCase.kt
@@ -0,0 +1,11 @@
+package com.example.rickandmortyapp.domain.usecases
+
+import com.example.rickandmortyapp.domain.repositories.CharactersRepository
+import javax.inject.Inject
+
+class GetCharactersUseCase @Inject constructor(
+ private val repository: CharactersRepository
+) {
+
+ operator fun invoke(name: String) = repository.getCharacters(name)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetEpisodesUseCase.kt b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetEpisodesUseCase.kt
new file mode 100644
index 0000000..f1c7e37
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetEpisodesUseCase.kt
@@ -0,0 +1,11 @@
+package com.example.rickandmortyapp.domain.usecases
+
+import com.example.rickandmortyapp.domain.repositories.EpisodesRepository
+import javax.inject.Inject
+
+class GetEpisodesUseCase @Inject constructor(
+ private val repository: EpisodesRepository
+) {
+
+ operator fun invoke(name: String) = repository.getEpisodes(name)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetLocationsUseCase.kt b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetLocationsUseCase.kt
new file mode 100644
index 0000000..adac942
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/domain/usecases/GetLocationsUseCase.kt
@@ -0,0 +1,11 @@
+package com.example.rickandmortyapp.domain.usecases
+
+import com.example.rickandmortyapp.domain.repositories.LocationsRepository
+import javax.inject.Inject
+
+class GetLocationsUseCase @Inject constructor(
+ private val repository: LocationsRepository
+) {
+
+ operator fun invoke(name: String) = repository.getLocations(name)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/state/UIState.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/state/UIState.kt
new file mode 100644
index 0000000..7874955
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/state/UIState.kt
@@ -0,0 +1,7 @@
+package com.example.rickandmortyapp.presentation.state
+
+sealed class UIState {
+ class Loading : UIState()
+ class Error(val error: String) : UIState()
+ class Success(val data: T) : UIState()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/activity/MainActivity.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/activity/MainActivity.kt
new file mode 100644
index 0000000..f1b03b6
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/activity/MainActivity.kt
@@ -0,0 +1,68 @@
+package com.example.rickandmortyapp.presentation.ui.activity
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.NavController
+import androidx.navigation.fragment.NavHostFragment
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.NavigationUI
+import androidx.navigation.ui.NavigationUI.setupWithNavController
+import by.kirich1409.viewbindingdelegate.viewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.databinding.ActivityMainBinding
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity(R.layout.activity_main) {
+
+ private lateinit var navController: NavController
+ private val binding by viewBinding(ActivityMainBinding::bind)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setupNavigation()
+ setupListeners()
+// supportActionBar?.setHomeAsUpIndicator(R.drawable.rickandmortytitle)
+// supportActionBar?.setHomeButtonEnabled(true)
+// supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+ private fun setupListeners() {
+ onDestinationChangedListener()
+ }
+
+ private fun setupNavigation() {
+ val navHostFragment =
+ supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
+ navController = navHostFragment.navController
+
+ val appBarConfiguration = AppBarConfiguration.Builder(
+ R.id.charactersFragment,
+ R.id.locationsFragment,
+ R.id.episodesFragment,
+ R.id.searchFragment
+ ).build()
+
+ setupWithNavController(binding.bottomNavigation, navController)
+ NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
+ }
+
+ private fun onDestinationChangedListener() {
+ navController.addOnDestinationChangedListener { _, destination, _ ->
+ when (destination.id) {
+ R.id.charactersFragment
+ -> {
+ }
+ R.id.locationsFragment
+ -> {
+ }
+ R.id.episodesFragment -> {
+ }
+ R.id.searchFragment -> {
+ }
+ else -> {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/CharactersAdapter.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/CharactersAdapter.kt
new file mode 100644
index 0000000..0047dc6
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/CharactersAdapter.kt
@@ -0,0 +1,85 @@
+package com.example.rickandmortyapp.presentation.ui.adapters
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.viewbinding.ViewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.base.BaseRecyclerViewHolder
+import com.example.rickandmortyapp.common.extensions.loadImage
+import com.example.rickandmortyapp.common.extensions.setOnSingleClickListener
+import com.example.rickandmortyapp.databinding.ItemCharactersBinding
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class CharactersAdapter(val onClick: (id: Int?) -> Unit) :
+ PagingDataAdapter>(
+ CharacterDiffUtil()
+ ) {
+
+ class CharacterDiffUtil : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(
+ oldItem: RickAndMorty.CharactersItem,
+ newItem: RickAndMorty.CharactersItem
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(
+ oldItem: RickAndMorty.CharactersItem,
+ newItem: RickAndMorty.CharactersItem
+ ): Boolean {
+ return oldItem == newItem
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): BaseRecyclerViewHolder {
+ return CharactersViewHolder(
+ ItemCharactersBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(
+ holder: BaseRecyclerViewHolder,
+ position: Int
+ ) {
+ getItem(position)?.let { holder.onBind(it) }
+ }
+
+ inner class CharactersViewHolder(
+ binding: ItemCharactersBinding
+ ) : BaseRecyclerViewHolder(binding) {
+
+ override fun onBind(item: RickAndMorty.CharactersItem) = with(binding) {
+ itemName.text = item.name
+ itemImage.loadImage(item.image)
+ itemStatus.text = item.status
+ itemSpecies.text = item.species
+ itemLocation.text = item.location.name
+ itemEpisode.text = item.origin.name
+
+ when (item.status) {
+ "Alive" -> statusDot.setImageResource(R.drawable.ic_dot)
+
+ "Dead" -> statusDot.setImageResource(R.drawable.ic_dot_red)
+
+ "unknown" -> statusDot.setImageResource(R.drawable.ic_dot_gray)
+ }
+ itemView.setOnSingleClickListener {
+ getItem(absoluteAdapterPosition)?.apply {
+ onClick(this.id)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/EpisodesAdapter.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/EpisodesAdapter.kt
new file mode 100644
index 0000000..0a67fa6
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/EpisodesAdapter.kt
@@ -0,0 +1,70 @@
+package com.example.rickandmortyapp.presentation.ui.adapters
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.viewbinding.ViewBinding
+import com.example.rickandmortyapp.base.BaseRecyclerViewHolder
+import com.example.rickandmortyapp.common.extensions.toFormatDate
+import com.example.rickandmortyapp.databinding.ItemEpisodesBinding
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class EpisodesAdapter :
+ PagingDataAdapter>(
+ EpisodeDiffUtil()
+ ) {
+
+ class EpisodeDiffUtil : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(
+ oldItem: RickAndMorty.EpisodesItem,
+ newItem: RickAndMorty.EpisodesItem
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(
+ oldItem: RickAndMorty.EpisodesItem,
+ newItem: RickAndMorty.EpisodesItem
+ ): Boolean {
+ return oldItem == newItem
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): BaseRecyclerViewHolder {
+ return EpisodesViewHolder(
+ ItemEpisodesBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(
+ holder: BaseRecyclerViewHolder,
+ position: Int
+ ) {
+ getItem(position)?.let { holder.onBind(it) }
+ }
+
+ inner class EpisodesViewHolder(
+ binding: ItemEpisodesBinding
+ ) : BaseRecyclerViewHolder(binding) {
+
+ override fun onBind(item: RickAndMorty.EpisodesItem) = with(binding) {
+ itemName.text = item.name
+ itemEpisode.text = item.episode
+ ?.replace("S", "Season ")
+ ?.replace("E", " Episode ")
+ itemCreated.text = toFormatDate(item.created)
+ itemAirDate.text = item.airDate
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/LocationsAdapter.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/LocationsAdapter.kt
new file mode 100644
index 0000000..2a9ac7b
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/LocationsAdapter.kt
@@ -0,0 +1,70 @@
+package com.example.rickandmortyapp.presentation.ui.adapters
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.viewbinding.ViewBinding
+import com.example.rickandmortyapp.base.BaseRecyclerViewHolder
+import com.example.rickandmortyapp.common.extensions.toFormatDate
+import com.example.rickandmortyapp.databinding.ItemLocationsBinding
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class LocationsAdapter :
+ PagingDataAdapter>(
+ LocationDiffUtil()
+ ) {
+
+ class LocationDiffUtil : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(
+ oldItem: RickAndMorty.LocationsItem,
+ newItem: RickAndMorty.LocationsItem
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(
+ oldItem: RickAndMorty.LocationsItem,
+ newItem: RickAndMorty.LocationsItem
+ ): Boolean {
+ return oldItem == newItem
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): BaseRecyclerViewHolder {
+ return LocationsViewHolder(
+ ItemLocationsBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(
+ holder: BaseRecyclerViewHolder,
+ position: Int
+ ) {
+ getItem(position)?.let { holder.onBind(it) }
+ }
+
+ inner class LocationsViewHolder(
+ binding: ItemLocationsBinding
+ ) : BaseRecyclerViewHolder(binding) {
+
+ override fun onBind(item: RickAndMorty.LocationsItem) = with(binding) {
+ itemName.text = item.name
+ itemType.text = item.type
+ imagePlanet.isVisible = item.type == "Planet"
+ itemCreated.text = toFormatDate(item.created)
+ itemDimension.text = item.dimension
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchAdapter.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchAdapter.kt
new file mode 100644
index 0000000..8cde6ad
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchAdapter.kt
@@ -0,0 +1,133 @@
+package com.example.rickandmortyapp.presentation.ui.adapters
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.viewbinding.ViewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.databinding.ItemCharactersBinding
+import com.example.rickandmortyapp.databinding.ItemEpisodesBinding
+import com.example.rickandmortyapp.databinding.ItemLocationsBinding
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+class SearchAdapter :
+ PagingDataAdapter>(
+ SearchDiffUtil()
+ ) {
+
+ class SearchDiffUtil : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: RickAndMorty, newItem: RickAndMorty): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: RickAndMorty, newItem: RickAndMorty): Boolean {
+ return oldItem == newItem
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): SearchRecyclerViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ return when (viewType) {
+ R.layout.item_characters -> SearchRecyclerViewHolder.CharactersViewHolder(
+ ItemCharactersBinding.inflate(
+ inflater,
+ parent,
+ false
+ )
+ )
+ R.layout.item_locations -> SearchRecyclerViewHolder.LocationsViewHolder(
+ ItemLocationsBinding.inflate(
+ inflater,
+ parent,
+ false
+ )
+ )
+ R.layout.item_episodes -> SearchRecyclerViewHolder.EpisodesViewHolder(
+ ItemEpisodesBinding.inflate(
+ inflater,
+ parent,
+ false
+ )
+ )
+ else -> throw IllegalAccessException("Invalid viewType provided")
+ }
+ }
+
+ override fun onBindViewHolder(
+ holder: SearchRecyclerViewHolder,
+ position: Int
+ ) {
+ when (holder) {
+ is SearchRecyclerViewHolder.CharactersViewHolder ->
+ holder.onBind(getItem(position) as RickAndMorty.CharactersItem)
+ is SearchRecyclerViewHolder.LocationsViewHolder ->
+ holder.onBind(getItem(position) as RickAndMorty.LocationsItem)
+ is SearchRecyclerViewHolder.EpisodesViewHolder ->
+ holder.onBind(getItem(position) as RickAndMorty.EpisodesItem)
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (getItem(position)) {
+ is RickAndMorty.CharactersItem -> R.layout.item_characters
+ is RickAndMorty.LocationsItem -> R.layout.item_locations
+ is RickAndMorty.EpisodesItem -> R.layout.item_episodes
+ null -> throw IllegalStateException("Unknown view")
+ }
+ }
+}
+
+//inner class CharactersViewHolder(
+// binding: ItemCharactersBinding
+//) : BaseRecyclerViewHolder(binding) {
+//
+// override fun onBind(item: RickAndMorty.CharactersItem) = with(binding) {
+// itemName.text = item.characters.name
+// itemImage.loadImage(item.characters.image)
+// itemStatus.text = item.characters.status
+// itemSpecies.text = item.characters.species
+// itemLocation.text = item.characters.location.name
+// itemEpisode.text = item.characters.origin.name
+//
+// when (item.characters.status) {
+//
+// "Alive" -> statusDot.setImageResource(R.drawable.ic_dot)
+//
+// "Dead" -> statusDot.setImageResource(R.drawable.ic_dot_red)
+//
+// "unknown" -> statusDot.setImageResource(R.drawable.ic_dot_gray)
+// }
+// }
+//}
+//
+//inner class LocationsViewHolder(
+// binding: ItemLocationsBinding
+//) : BaseRecyclerViewHolder(binding) {
+//
+// override fun onBind(item: RickAndMorty.LocationsItem) = with(binding) {
+// itemName.text = item.locations.name
+// itemType.text = item.locations.type
+// imagePlanet.isVisible = item.locations.type == "Planet"
+// itemCreated.text = toFormatDate(item.locations.created)
+// itemDimension.text = item.locations.dimension
+// }
+//}
+//
+//inner class EpisodesViewHolder(
+// binding: ItemEpisodesBinding
+//) : BaseRecyclerViewHolder(binding) {
+//
+// override fun onBind(item: RickAndMorty.EpisodesItem) = with(binding) {
+// itemName.text = item.episodes.name
+// itemEpisode.text = item.episodes.episode
+// ?.replace("S", "Season ")
+// ?.replace("E", " Episode ")
+// itemCreated.text = toFormatDate(item.episodes.created)
+// itemAirDate.text = item.episodes.airDate
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchRecyclerViewHolder.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchRecyclerViewHolder.kt
new file mode 100644
index 0000000..bc6189b
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/SearchRecyclerViewHolder.kt
@@ -0,0 +1,66 @@
+package com.example.rickandmortyapp.presentation.ui.adapters
+
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.common.extensions.loadImage
+import com.example.rickandmortyapp.common.extensions.toFormatDate
+import com.example.rickandmortyapp.databinding.ItemCharactersBinding
+import com.example.rickandmortyapp.databinding.ItemEpisodesBinding
+import com.example.rickandmortyapp.databinding.ItemLocationsBinding
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+
+sealed class SearchRecyclerViewHolder(binding: V) :
+ RecyclerView.ViewHolder(binding.root) {
+
+ class CharactersViewHolder(
+ private val binding: ItemCharactersBinding
+ ) : SearchRecyclerViewHolder(binding) {
+
+ fun onBind(item: RickAndMorty.CharactersItem) = with(binding) {
+ itemName.text = item.name
+ itemImage.loadImage(item.image)
+ itemStatus.text = item.status
+ itemSpecies.text = item.species
+ itemLocation.text = item.location.name
+ itemEpisode.text = item.origin.name
+
+ when (item.status) {
+
+ "Alive" -> statusDot.setImageResource(R.drawable.ic_dot)
+
+ "Dead" -> statusDot.setImageResource(R.drawable.ic_dot_red)
+
+ "unknown" -> statusDot.setImageResource(R.drawable.ic_dot_gray)
+ }
+ }
+ }
+
+ class LocationsViewHolder(
+ private val binding: ItemLocationsBinding
+ ) : SearchRecyclerViewHolder(binding) {
+
+ fun onBind(item: RickAndMorty.LocationsItem) = with(binding) {
+ itemName.text = item.name
+ itemType.text = item.type
+ imagePlanet.isVisible = item.type == "Planet"
+ itemCreated.text = toFormatDate(item.created)
+ itemDimension.text = item.dimension
+ }
+ }
+
+ class EpisodesViewHolder(
+ private val binding: ItemEpisodesBinding
+ ) : SearchRecyclerViewHolder(binding) {
+
+ fun onBind(item: RickAndMorty.EpisodesItem) = with(binding) {
+ itemName.text = item.name
+ itemEpisode.text = item.episode
+ ?.replace("S", "Season ")
+ ?.replace("E", " Episode ")
+ itemCreated.text = toFormatDate(item.created)
+ itemAirDate.text = item.airDate
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateAdapter.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateAdapter.kt
new file mode 100644
index 0000000..4b28214
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateAdapter.kt
@@ -0,0 +1,16 @@
+package com.example.rickandmortyapp.presentation.ui.adapters.paging
+
+import android.view.ViewGroup
+import androidx.paging.LoadState
+import androidx.paging.LoadStateAdapter
+
+class LoadStateAdapter(private val retry: () -> Unit) : LoadStateAdapter() {
+
+ override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
+ holder.onBind(loadState)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
+ return LoadStateViewHolder.create(parent, retry)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateViewHolder.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateViewHolder.kt
new file mode 100644
index 0000000..179e0f8
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/adapters/paging/LoadStateViewHolder.kt
@@ -0,0 +1,40 @@
+package com.example.rickandmortyapp.presentation.ui.adapters.paging
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.paging.LoadState
+import androidx.recyclerview.widget.RecyclerView
+import com.example.rickandmortyapp.common.extensions.setOnSingleClickListener
+import com.example.rickandmortyapp.databinding.ItemLoadFooterBinding
+
+class LoadStateViewHolder(
+ private val binding: ItemLoadFooterBinding,
+ retry: () -> Unit
+) : RecyclerView.ViewHolder(binding.root) {
+
+ init {
+ binding.btnRetry.setOnSingleClickListener {
+ retry.invoke()
+ }
+ }
+
+ fun onBind(loadState: LoadState) {
+ binding.progressBar.isVisible = loadState is LoadState.Loading
+ binding.btnRetry.isVisible = loadState is LoadState.Error
+ binding.errorMsg.isVisible = loadState is LoadState.Error
+ }
+
+ companion object {
+ fun create(parent: ViewGroup, retry: () -> Unit): LoadStateViewHolder {
+ return LoadStateViewHolder(
+ ItemLoadFooterBinding.inflate(
+ LayoutInflater
+ .from(parent.context), parent,
+ false
+ ), retry
+ )
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersFragment.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersFragment.kt
new file mode 100644
index 0000000..c48864c
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersFragment.kt
@@ -0,0 +1,92 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.characters
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.widget.SearchView
+import androidx.core.view.isVisible
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.paging.LoadState
+import androidx.recyclerview.widget.LinearLayoutManager
+import by.kirich1409.viewbindingdelegate.viewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.base.BaseFragment
+import com.example.rickandmortyapp.common.extensions.hideKeyboard
+import com.example.rickandmortyapp.common.extensions.setOnActionExpandListener
+import com.example.rickandmortyapp.common.extensions.setTools
+import com.example.rickandmortyapp.common.extensions.submitSearch
+import com.example.rickandmortyapp.databinding.FragmentCharactersBinding
+import com.example.rickandmortyapp.presentation.ui.adapters.CharactersAdapter
+import com.example.rickandmortyapp.presentation.ui.adapters.paging.LoadStateAdapter
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class CharactersFragment : BaseFragment(
+ R.layout.fragment_characters
+) {
+
+ override val binding by viewBinding(FragmentCharactersBinding::bind)
+ override val viewModel by viewModels()
+ private val adapter = CharactersAdapter(this::onClickToDetail)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun initialize() {
+ setupRecycler()
+ }
+
+ private fun setupRecycler() = with(binding) {
+ binding.charactersRecycler.layoutManager = LinearLayoutManager(requireContext())
+ binding.charactersRecycler.adapter = adapter.withLoadStateFooter(LoadStateAdapter {
+ adapter.retry()
+ })
+ }
+
+ override fun setupViews() {
+ setupProgressBar()
+ }
+
+ private fun setupProgressBar() = with(binding) {
+ adapter.addLoadStateListener {
+ if (view != null) {
+ charactersLoading.isVisible = it.refresh is LoadState.Loading
+ container.containerNotFound.isVisible = it.refresh is LoadState.Error
+ charactersRecycler.isVisible = it.refresh !is LoadState.Error
+ }
+ }
+ }
+
+ override fun setupRequests() {
+ viewModel.getCharacters("")
+ }
+
+ override fun setupObserves() {
+ viewModel.charactersState.observe(viewLifecycleOwner, {
+ lifecycleScope.launch {
+ adapter.submitData(it)
+ }
+ })
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.toolbar_menu, menu)
+ val searchItem = menu.findItem(R.id.search)
+ val searchView = searchItem.actionView as SearchView
+
+ searchView.setTools(context)
+
+ searchView.submitSearch { viewModel.getCharacters(it.toString()) }
+
+ searchItem.setOnActionExpandListener(searchView) { hideKeyboard() }
+ }
+
+ private fun onClickToDetail(id: Int?) {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersViewModel.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersViewModel.kt
new file mode 100644
index 0000000..636bcca
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/characters/CharactersViewModel.kt
@@ -0,0 +1,28 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.characters
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.rickandmortyapp.base.BaseViewModel
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import com.example.rickandmortyapp.domain.usecases.GetCharactersUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class CharactersViewModel @Inject constructor(
+ private val getAllCharacters: GetCharactersUseCase
+) : BaseViewModel() {
+
+ private val _charactersState = MutableLiveData>()
+ val charactersState: LiveData> = _charactersState
+
+ fun getCharacters(name: String) = viewModelScope.launch {
+ getAllCharacters(name).collect {
+ _charactersState.value = it as PagingData
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesFragment.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesFragment.kt
new file mode 100644
index 0000000..847b149
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesFragment.kt
@@ -0,0 +1,86 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.episodes
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.widget.SearchView
+import androidx.core.view.isVisible
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.paging.LoadState
+import androidx.recyclerview.widget.LinearLayoutManager
+import by.kirich1409.viewbindingdelegate.viewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.base.BaseFragment
+import com.example.rickandmortyapp.common.extensions.hideKeyboard
+import com.example.rickandmortyapp.common.extensions.setOnActionExpandListener
+import com.example.rickandmortyapp.common.extensions.setTools
+import com.example.rickandmortyapp.common.extensions.submitSearch
+import com.example.rickandmortyapp.databinding.FragmentEpisodesBinding
+import com.example.rickandmortyapp.presentation.ui.adapters.EpisodesAdapter
+import com.example.rickandmortyapp.presentation.ui.adapters.paging.LoadStateAdapter
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class EpisodesFragment : BaseFragment(
+ R.layout.fragment_episodes
+) {
+
+ override val binding by viewBinding(FragmentEpisodesBinding::bind)
+ override val viewModel by viewModels()
+ private val adapter: EpisodesAdapter = EpisodesAdapter()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun initialize() {
+ setupRecycler()
+ }
+
+ private fun setupRecycler() = with(binding) {
+ episodesRecycler.layoutManager = LinearLayoutManager(requireContext())
+ episodesRecycler.adapter = adapter.withLoadStateFooter(LoadStateAdapter {
+ adapter.retry()
+ })
+ }
+
+ override fun setupViews() {
+ setupProgressBar()
+ }
+
+ private fun setupProgressBar() {
+ adapter.addLoadStateListener {
+ if (view != null) {
+ binding.episodesLoading.isVisible = it.refresh is LoadState.Loading
+ }
+ }
+ }
+
+ override fun setupRequests() {
+ viewModel.getEpisodes("")
+ }
+
+ override fun setupObserves() {
+ viewModel.episodesSate.observe(viewLifecycleOwner, {
+ lifecycleScope.launch {
+ adapter.submitData(it)
+ }
+ })
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.toolbar_menu, menu)
+ val searchItem = menu.findItem(R.id.search)
+ val searchView = searchItem.actionView as SearchView
+
+ searchView.setTools(context)
+
+ searchView.submitSearch { viewModel.getEpisodes(it.toString()) }
+
+ searchItem.setOnActionExpandListener(searchView) { hideKeyboard() }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesViewModel.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesViewModel.kt
new file mode 100644
index 0000000..69c96ac
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/episodes/EpisodesViewModel.kt
@@ -0,0 +1,28 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.episodes
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.rickandmortyapp.base.BaseViewModel
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import com.example.rickandmortyapp.domain.usecases.GetEpisodesUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class EpisodesViewModel @Inject constructor(
+ private val getAllEpisodes: GetEpisodesUseCase
+) : BaseViewModel() {
+
+ private val _episodesState = MutableLiveData>()
+ val episodesSate: LiveData> = _episodesState
+
+ fun getEpisodes(name: String) = viewModelScope.launch {
+ getAllEpisodes(name).collect {
+ _episodesState.value = it as PagingData
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsFragment.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsFragment.kt
new file mode 100644
index 0000000..3a84771
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsFragment.kt
@@ -0,0 +1,85 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.locations
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.widget.SearchView
+import androidx.core.view.isVisible
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.paging.LoadState
+import androidx.recyclerview.widget.LinearLayoutManager
+import by.kirich1409.viewbindingdelegate.viewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.base.BaseFragment
+import com.example.rickandmortyapp.common.extensions.hideKeyboard
+import com.example.rickandmortyapp.common.extensions.setOnActionExpandListener
+import com.example.rickandmortyapp.common.extensions.setTools
+import com.example.rickandmortyapp.common.extensions.submitSearch
+import com.example.rickandmortyapp.databinding.FragmentLocationsBinding
+import com.example.rickandmortyapp.presentation.ui.adapters.LocationsAdapter
+import com.example.rickandmortyapp.presentation.ui.adapters.paging.LoadStateAdapter
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class LocationsFragment :
+ BaseFragment(R.layout.fragment_locations) {
+
+ override val binding by viewBinding(FragmentLocationsBinding::bind)
+ override val viewModel by viewModels()
+ private val adapter: LocationsAdapter = LocationsAdapter()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun initialize() {
+ setupRecycler()
+ }
+
+ private fun setupRecycler() = with(binding) {
+ locationsRecycler.layoutManager = LinearLayoutManager(requireContext())
+ locationsRecycler.adapter = adapter.withLoadStateFooter(LoadStateAdapter {
+ adapter.retry()
+ })
+ }
+
+ override fun setupViews() {
+ setupProgressBar()
+ }
+
+ private fun setupProgressBar() {
+ adapter.addLoadStateListener {
+ if (view != null) {
+ binding.locationsLoading.isVisible = it.refresh is LoadState.Loading
+ }
+ }
+ }
+
+ override fun setupRequests() {
+ viewModel.getLocations("")
+ }
+
+ override fun setupObserves() {
+ viewModel.locationsState.observe(viewLifecycleOwner, {
+ lifecycleScope.launch {
+ adapter.submitData(it)
+ }
+ })
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.toolbar_menu, menu)
+ val searchItem = menu.findItem(R.id.search)
+ val searchView = searchItem.actionView as SearchView
+
+ searchView.setTools(context)
+
+ searchView.submitSearch { viewModel.getLocations(it.toString()) }
+
+ searchItem.setOnActionExpandListener(searchView) { hideKeyboard() }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsViewModel.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsViewModel.kt
new file mode 100644
index 0000000..8113f93
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/locations/LocationsViewModel.kt
@@ -0,0 +1,29 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.locations
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.rickandmortyapp.base.BaseViewModel
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import com.example.rickandmortyapp.domain.usecases.GetLocationsUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+
+@HiltViewModel
+class LocationsViewModel @Inject constructor(
+ private val getAllLocations: GetLocationsUseCase
+) : BaseViewModel() {
+
+ private val _locationsState = MutableLiveData>()
+ val locationsState: LiveData> = _locationsState
+
+ fun getLocations(name: String) = viewModelScope.launch {
+ getAllLocations(name).collect {
+ _locationsState.value = it as PagingData
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchFragment.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchFragment.kt
new file mode 100644
index 0000000..eecd704
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchFragment.kt
@@ -0,0 +1,71 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.search
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.widget.SearchView
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import by.kirich1409.viewbindingdelegate.viewBinding
+import com.example.rickandmortyapp.R
+import com.example.rickandmortyapp.base.BaseFragment
+import com.example.rickandmortyapp.common.extensions.hideKeyboard
+import com.example.rickandmortyapp.common.extensions.setOnActionExpandListener
+import com.example.rickandmortyapp.common.extensions.setTools
+import com.example.rickandmortyapp.common.extensions.submitSearch
+import com.example.rickandmortyapp.databinding.FragmentSearchBinding
+import com.example.rickandmortyapp.presentation.ui.adapters.SearchAdapter
+import com.example.rickandmortyapp.presentation.ui.adapters.paging.LoadStateAdapter
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class SearchFragment : BaseFragment(
+ R.layout.fragment_search
+) {
+ override val binding by viewBinding(FragmentSearchBinding::bind)
+ override val viewModel by viewModels()
+ private val adapter: SearchAdapter = SearchAdapter()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun initialize() {
+ setupRecycler()
+ }
+
+ private fun setupRecycler() = with(binding) {
+ searchRecycler.layoutManager = LinearLayoutManager(requireContext())
+ searchRecycler.adapter = adapter.withLoadStateFooter(LoadStateAdapter {
+ adapter.retry()
+ })
+ }
+
+ override fun setupRequests() {
+ viewModel.processAllRequest("")
+ }
+
+ override fun setupObserves() {
+ viewModel.allState.observe(viewLifecycleOwner, {
+ lifecycleScope.launch {
+ adapter.submitData(it)
+ }
+ })
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.toolbar_menu, menu)
+ val searchItem = menu.findItem(R.id.search)
+ val searchView = searchItem.actionView as SearchView
+
+ searchView.setTools(context)
+
+ searchView.submitSearch { viewModel.processAllRequest(it.toString()) }
+
+ searchItem.setOnActionExpandListener(searchView) { hideKeyboard() }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchViewModel.kt b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchViewModel.kt
new file mode 100644
index 0000000..82fb0a1
--- /dev/null
+++ b/app/src/main/java/com/example/rickandmortyapp/presentation/ui/fragments/search/SearchViewModel.kt
@@ -0,0 +1,55 @@
+package com.example.rickandmortyapp.presentation.ui.fragments.search
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.rickandmortyapp.base.BaseViewModel
+import com.example.rickandmortyapp.domain.models.RickAndMorty
+import com.example.rickandmortyapp.domain.usecases.GetCharactersUseCase
+import com.example.rickandmortyapp.domain.usecases.GetEpisodesUseCase
+import com.example.rickandmortyapp.domain.usecases.GetLocationsUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+@HiltViewModel
+class SearchViewModel @Inject constructor(
+ private val getAllCharacters: GetCharactersUseCase,
+ private val getAllLocations: GetLocationsUseCase,
+ private val getAllEpisodes: GetEpisodesUseCase
+) : BaseViewModel() {
+
+ private val _allState = MutableLiveData>()
+ val allState: LiveData> = _allState
+
+ fun processAllRequest(name: String) = viewModelScope.launch {
+ val charactersDeferred =
+ withContext(Dispatchers.IO) {
+ getAllCharacters(name)
+ }
+ val locationsDeferred =
+ withContext(Dispatchers.IO) {
+ getAllLocations(name)
+ }
+ val episodesDeferred =
+ withContext(Dispatchers.IO) {
+ getAllEpisodes(name)
+ }
+
+ charactersDeferred.collect {
+ _allState.postValue(it as PagingData)
+ }
+
+ locationsDeferred.collect {
+ _allState.postValue(it as PagingData)
+ }
+
+ episodesDeferred.collect {
+ _allState.postValue(it as PagingData)
+ }
+ }
+}
\ 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 0000000..2b068d1
--- /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/bottom_nav_background.xml b/app/src/main/res/drawable/bottom_nav_background.xml
new file mode 100644
index 0000000..386f06d
--- /dev/null
+++ b/app/src/main/res/drawable/bottom_nav_background.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_dot.xml b/app/src/main/res/drawable/ic_dot.xml
new file mode 100644
index 0000000..8e81e0a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dot.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_dot_gray.xml b/app/src/main/res/drawable/ic_dot_gray.xml
new file mode 100644
index 0000000..0101cd8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dot_gray.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_dot_red.xml b/app/src/main/res/drawable/ic_dot_red.xml
new file mode 100644
index 0000000..2ce2623
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dot_red.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_earth.xml b/app/src/main/res/drawable/ic_earth.xml
new file mode 100644
index 0000000..b3555d5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_earth.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_episodes.xml b/app/src/main/res/drawable/ic_episodes.xml
new file mode 100644
index 0000000..2674bb3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_episodes.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml
new file mode 100644
index 0000000..9b3109d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_filter.xml
@@ -0,0 +1,9 @@
+
+
+
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 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml
new file mode 100644
index 0000000..6cece16
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_morty_smith.xml b/app/src/main/res/drawable/ic_morty_smith.xml
new file mode 100644
index 0000000..ced3f0d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_morty_smith.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml
new file mode 100644
index 0000000..e787317
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/rick_prison.jpg b/app/src/main/res/drawable/rick_prison.jpg
new file mode 100644
index 0000000..37f4346
Binary files /dev/null and b/app/src/main/res/drawable/rick_prison.jpg differ
diff --git a/app/src/main/res/drawable/rickandmorty.png b/app/src/main/res/drawable/rickandmorty.png
new file mode 100644
index 0000000..0cc0551
Binary files /dev/null and b/app/src/main/res/drawable/rickandmorty.png differ
diff --git a/app/src/main/res/drawable/rickandmortytitle.png b/app/src/main/res/drawable/rickandmortytitle.png
new file mode 100644
index 0000000..31613c9
Binary files /dev/null and b/app/src/main/res/drawable/rickandmortytitle.png differ
diff --git a/app/src/main/res/drawable/search_view_background.xml b/app/src/main/res/drawable/search_view_background.xml
new file mode 100644
index 0000000..8ec35ec
--- /dev/null
+++ b/app/src/main/res/drawable/search_view_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/abril_fatface.xml b/app/src/main/res/font/abril_fatface.xml
new file mode 100644
index 0000000..2299e59
--- /dev/null
+++ b/app/src/main/res/font/abril_fatface.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/anton.xml b/app/src/main/res/font/anton.xml
new file mode 100644
index 0000000..2c6425b
--- /dev/null
+++ b/app/src/main/res/font/anton.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/archivo_black.xml b/app/src/main/res/font/archivo_black.xml
new file mode 100644
index 0000000..d707493
--- /dev/null
+++ b/app/src/main/res/font/archivo_black.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/bowlby_one.xml b/app/src/main/res/font/bowlby_one.xml
new file mode 100644
index 0000000..dc72d9b
--- /dev/null
+++ b/app/src/main/res/font/bowlby_one.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/bowlby_one_sc.xml b/app/src/main/res/font/bowlby_one_sc.xml
new file mode 100644
index 0000000..e6fdb52
--- /dev/null
+++ b/app/src/main/res/font/bowlby_one_sc.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/bungee.xml b/app/src/main/res/font/bungee.xml
new file mode 100644
index 0000000..465b0f5
--- /dev/null
+++ b/app/src/main/res/font/bungee.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/candal.xml b/app/src/main/res/font/candal.xml
new file mode 100644
index 0000000..1d2cbfe
--- /dev/null
+++ b/app/src/main/res/font/candal.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/erica_one.xml b/app/src/main/res/font/erica_one.xml
new file mode 100644
index 0000000..0e54546
--- /dev/null
+++ b/app/src/main/res/font/erica_one.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/passion_one.xml b/app/src/main/res/font/passion_one.xml
new file mode 100644
index 0000000..8e72ea9
--- /dev/null
+++ b/app/src/main/res/font/passion_one.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/russo_one.xml b/app/src/main/res/font/russo_one.xml
new file mode 100644
index 0000000..0ccc478
--- /dev/null
+++ b/app/src/main/res/font/russo_one.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/squada_one.xml b/app/src/main/res/font/squada_one.xml
new file mode 100644
index 0000000..60211d9
--- /dev/null
+++ b/app/src/main/res/font/squada_one.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/font/titan_one.xml b/app/src/main/res/font/titan_one.xml
new file mode 100644
index 0000000..11dcb62
--- /dev/null
+++ b/app/src/main/res/font/titan_one.xml
@@ -0,0 +1,7 @@
+
+
+
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 0000000..be5b7a8
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/container_not_found.xml b/app/src/main/res/layout/container_not_found.xml
new file mode 100644
index 0000000..a8457fa
--- /dev/null
+++ b/app/src/main/res/layout/container_not_found.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_characters.xml b/app/src/main/res/layout/fragment_characters.xml
new file mode 100644
index 0000000..c9777b1
--- /dev/null
+++ b/app/src/main/res/layout/fragment_characters.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_episodes.xml b/app/src/main/res/layout/fragment_episodes.xml
new file mode 100644
index 0000000..be1f872
--- /dev/null
+++ b/app/src/main/res/layout/fragment_episodes.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_locations.xml b/app/src/main/res/layout/fragment_locations.xml
new file mode 100644
index 0000000..5b765bc
--- /dev/null
+++ b/app/src/main/res/layout/fragment_locations.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
new file mode 100644
index 0000000..bb1a3f4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_characters.xml b/app/src/main/res/layout/item_characters.xml
new file mode 100644
index 0000000..6893369
--- /dev/null
+++ b/app/src/main/res/layout/item_characters.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_episodes.xml b/app/src/main/res/layout/item_episodes.xml
new file mode 100644
index 0000000..0e6443f
--- /dev/null
+++ b/app/src/main/res/layout/item_episodes.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_load_footer.xml b/app/src/main/res/layout/item_load_footer.xml
new file mode 100644
index 0000000..7f94cb5
--- /dev/null
+++ b/app/src/main/res/layout/item_load_footer.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_locations.xml b/app/src/main/res/layout/item_locations.xml
new file mode 100644
index 0000000..5c96314
--- /dev/null
+++ b/app/src/main/res/layout/item_locations.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml
new file mode 100644
index 0000000..b03e8c8
--- /dev/null
+++ b/app/src/main/res/menu/bottom_navigation_menu.xml
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/toolbar_menu.xml b/app/src/main/res/menu/toolbar_menu.xml
new file mode 100644
index 0000000..6c675c4
--- /dev/null
+++ b/app/src/main/res/menu/toolbar_menu.xml
@@ -0,0 +1,18 @@
+
+
\ 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 0000000..eca70cf
--- /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_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /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.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..c348272
--- /dev/null
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ 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 0000000..96c462f
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ 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 0000000..2e4125a
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,15 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #393c42
+ #1f232a
+ #0C0D0E
+ #00b2c9
+ #9e9f9a
+
\ 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 0000000..2f1e4b2
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,17 @@
+
+
+ 150dp
+ 10dp
+ 17sp
+ 25sp
+ 10sp
+ 12sp
+ 4dp
+ 14dp
+ 2dp
+ 18dp
+ 38dp
+ 17dp
+ 3dp
+ 52dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/font_certs.xml b/app/src/main/res/values/font_certs.xml
new file mode 100644
index 0000000..d2226ac
--- /dev/null
+++ b/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,17 @@
+
+
+
+ - @array/com_google_android_gms_fonts_certs_dev
+ - @array/com_google_android_gms_fonts_certs_prod
+
+
+ -
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+ -
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/app/src/main/res/values/preloaded_fonts.xml b/app/src/main/res/values/preloaded_fonts.xml
new file mode 100644
index 0000000..433779e
--- /dev/null
+++ b/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,18 @@
+
+
+
+ - @font/
+ - @font/abril_fatface
+ - @font/anton
+ - @font/archivo_black
+ - @font/bowlby_one
+ - @font/bowlby_one_sc
+ - @font/bungee
+ - @font/candal
+ - @font/erica_one
+ - @font/passion_one
+ - @font/russo_one
+ - @font/squada_one
+ - @font/titan_one
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..644177c
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,27 @@
+
+ Rick & Morty
+ Персонажи
+ Локации
+ Эпизоды
+ Поиск
+
+ Hello blank fragment
+ Rick & Morty
+ Jonatan Gray
+ Alive
+ Нет подключения к Интернету!
+ RETRY
+ Last known location:
+ Human
+ Earth
+ First seen in:
+ Type:
+ Dimension:
+ Created:
+ Was created in:
+ Episode
+ The air date was:
+ Поиск...
+ No results found
+ 404
+
\ 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 0000000..f053fdf
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/example/rickandmortyapp/ExampleUnitTest.kt b/app/src/test/java/com/example/rickandmortyapp/ExampleUnitTest.kt
new file mode 100644
index 0000000..cafea45
--- /dev/null
+++ b/app/src/test/java/com/example/rickandmortyapp/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.rickandmortyapp
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * 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)
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..37bdb5c
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext {
+ kotlin_version = "1.6.0"
+ nav_version = "2.3.5"
+ hilt_version = '2.38.1'
+ }
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:7.0.4"
+
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // Hilt
+ classpath("com.google.dagger:hilt-android-gradle-plugin:$hilt_version")
+
+ // Navigation Safe Args
+ classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
+ }
+}
+
+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 0000000..98bed16
--- /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 0000000..e708b1c
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 0000000..92600bf
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Jan 04 17:55:50 ALMT 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## 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='"-Xmx64m" "-Xms64m"'
+
+# 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 or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@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 Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@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="-Xmx64m" "-Xms64m"
+
+@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 execute
+
+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 execute
+
+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
+
+: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 %*
+
+: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 0000000..addfb21
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,10 @@
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter() // Warning: this repository is going to shut down soon
+ }
+}
+rootProject.name = "RickAndMortyApp"
+include ':app'