diff --git a/build.gradle b/build.gradle index 2242038..44813b1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,11 @@ buildscript { maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath GradleDependencies.androidGradlePlugin - classpath KotlinDependencies.kotlinGradlePlugin - classpath SupportDependencies.googleService - classpath SupportDependencies.fabricTools + classpath ClasspathDependencies.androidGradlePlugin + classpath ClasspathDependencies.kotlinGradlePlugin + classpath ClasspathDependencies.googleService + classpath ClasspathDependencies.fabricTools + classpath ClasspathDependencies.safeArgs } } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index abf0847..bfb898a 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -22,7 +22,6 @@ object Versions { internal const val testJunit = "1.1.1" internal const val robolectric = "4.3.1" internal const val coroutinesTest = "1.2.1" - internal const val lifecycleVersion = "2.1.0" internal const val firebaseAnalytics = "17.2.0" internal const val crashlytics = "2.10.1" internal const val googleService = "4.3.2" @@ -30,14 +29,24 @@ object Versions { internal const val materialVersion = "1.0.0" internal const val cardView = "28.0.0" internal const val roomVersion = "2.2.3" -} -object GradleDependencies { - const val androidGradlePlugin = "com.android.tools.build:gradle:${Versions.androidGradlePlugin}" + // Jetpack + internal const val lifecycleVersion = "2.1.0" + internal const val navigationVersion = "2.1.0" } object JetPackDependencies { const val lifecycle = "androidx.lifecycle:lifecycle-extensions:${Versions.lifecycleVersion}" + const val navigationFragment = "androidx.navigation:navigation-fragment-ktx:${Versions.navigationVersion}" + const val navigationUi = "androidx.navigation:navigation-ui-ktx:${Versions.navigationVersion}" +} + +object ClasspathDependencies { + const val androidGradlePlugin = "com.android.tools.build:gradle:${Versions.androidGradlePlugin}" + const val fabricTools = "io.fabric.tools:gradle:${Versions.fabricTools}" + const val googleService = "com.google.gms:google-services:${Versions.googleService}" + const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" + const val safeArgs = "androidx.navigation:navigation-safe-args-gradle-plugin:${Versions.navigationVersion}" } object SupportDependencies { @@ -49,13 +58,10 @@ object SupportDependencies { const val cardView = "com.android.support:cardview-v7:${Versions.cardView}" const val firebaseAnalytics = "com.google.firebase:firebase-analytics:${Versions.firebaseAnalytics}" const val crashlytics = "com.crashlytics.sdk.android:crashlytics:${Versions.crashlytics}" - const val fabricTools = "io.fabric.tools:gradle:${Versions.fabricTools}" - const val googleService = "com.google.gms:google-services:${Versions.googleService}" } object KotlinDependencies { const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}" - const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}" } diff --git a/presentation/build.gradle b/presentation/build.gradle index bebf006..1dc51f7 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.google.gms.google-services' apply plugin: 'io.fabric' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: "androidx.navigation.safeargs.kotlin" apply from: "$rootDir/detekt.gradle" android { @@ -23,9 +24,19 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } } dependencies { + implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation project(":domain") implementation project(":data") implementation fileTree(dir: 'libs', include: ['*.jar']) @@ -33,6 +44,8 @@ dependencies { implementation KotlinDependencies.kotlinStdLib implementation JetPackDependencies.lifecycle + implementation JetPackDependencies.navigationFragment + implementation JetPackDependencies.navigationUi implementation SupportDependencies.coreKtx implementation SupportDependencies.appCompat diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index d23cd0a..b0149ce 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -15,17 +15,13 @@ android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> - + - - - + - - \ No newline at end of file diff --git a/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesActivity.kt deleted file mode 100644 index e391ea4..0000000 --- a/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesActivity.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.architect.coders.mu8.categories - -import android.os.Bundle -import android.widget.TextView -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.architect.coders.mu8.R -import com.architect.coders.mu8.categories.CategoriesViewModel.UiModel -import com.architect.coders.mu8.characters.CharactersActivity -import com.architect.coders.mu8.comics.ComicsActivity -import com.architect.coders.mu8.data.categories.CategoriesRepository -import com.architect.coders.mu8.events.EventsActivity -import com.architect.coders.mu8.utils.getViewModel -import com.architect.coders.mu8.utils.startActivity -import com.architect.codes.mu8.utils.CHARACTERS -import com.architect.codes.mu8.utils.COMICS -import com.architect.codes.mu8.utils.EVENTS -import com.architect.codes.mu8.utils.NOT_FOUND - -class CategoriesActivity : AppCompatActivity() { - - private lateinit var toolbar: Toolbar - private lateinit var toolbarTitle: TextView - private lateinit var recycler: RecyclerView - - private lateinit var viewModel: CategoriesViewModel - private lateinit var adapter: CategoriesAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_categories) - - toolbar = findViewById(R.id.toolbar) - setSupportActionBar(toolbar) - supportActionBar?.setDisplayShowTitleEnabled(false) - - toolbarTitle = findViewById(R.id.toolbar_title) - toolbarTitle.text = getString(R.string.app_name) - - recycler = findViewById(R.id.marvel_list) - - viewModel = getViewModel { CategoriesViewModel(CategoriesRepository()) } - - adapter = CategoriesAdapter(viewModel::onCategoryClicked) - recycler.layoutManager = LinearLayoutManager(this) - recycler.adapter = adapter - - viewModel.model.observe(this, Observer(::updateUi)) - } - - private fun updateUi(model: UiModel) { - when (model) { - is UiModel.Content -> { - adapter.categories = model.categories - } - is UiModel.Navigation -> { - when (model.categoryName) { - CHARACTERS -> startActivity {} - COMICS -> startActivity {} - EVENTS -> startActivity {} - else -> Toast.makeText(this, NOT_FOUND, Toast.LENGTH_SHORT).show() - } - } - } - } -} diff --git a/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesFragment.kt new file mode 100644 index 0000000..72a6144 --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/categories/CategoriesFragment.kt @@ -0,0 +1,70 @@ +package com.architect.coders.mu8.categories + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.lifecycle.Observer +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.architect.coders.mu8.R +import com.architect.coders.mu8.common.NavigationFragment +import com.architect.coders.mu8.common.StartingNavHostActivity +import com.architect.coders.mu8.data.categories.CategoriesRepository +import com.architect.coders.mu8.utils.getViewModel +import com.architect.codes.mu8.utils.CHARACTERS +import com.architect.codes.mu8.utils.COMICS +import com.architect.codes.mu8.utils.EVENTS +import com.architect.codes.mu8.utils.NOT_FOUND + +class CategoriesFragment : NavigationFragment() { + + private lateinit var recycler: RecyclerView + + private lateinit var viewModel: CategoriesViewModel + private lateinit var adapter: CategoriesAdapter + + private lateinit var navigationController: NavController + private lateinit var callback: OnBackPressedCallback + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_list, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val navHostActivity = activity as StartingNavHostActivity + navHostActivity.showToolbar() + navHostActivity.setToolbarTitle(getString(R.string.app_name)) + + navigationController = view.findNavController() + + viewModel = getViewModel { CategoriesViewModel(CategoriesRepository()) } + + adapter = CategoriesAdapter(viewModel::onCategoryClicked) + + recycler = view.findViewById(R.id.recycler) + recycler.layoutManager = LinearLayoutManager(navHostActivity) + recycler.adapter = adapter + + viewModel.model.observe(this, Observer(::updateUi)) + } + + private fun updateUi(model: CategoriesViewModel.UiModel) { + val context = this.context ?: return + when (model) { + is CategoriesViewModel.UiModel.Content -> adapter.categories = model.categories + is CategoriesViewModel.UiModel.Navigation -> { + when (model.categoryName) { + CHARACTERS -> navigationController.navigate(R.id.action_categoriesFragment_to_charactersFragment) + COMICS -> navigationController.navigate(R.id.action_categoriesFragment_to_comicsFragment) + EVENTS -> navigationController.navigate(R.id.action_categoriesFragment_to_eventsFragment) + else -> Toast.makeText(context, NOT_FOUND, Toast.LENGTH_SHORT).show() + } + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersActivity.kt deleted file mode 100644 index 59dd739..0000000 --- a/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersActivity.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.architect.coders.mu8.characters - -import android.os.Bundle -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.ProgressBar -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.architect.coders.mu8.R -import com.architect.coders.mu8.characters.CharactersUiModel.Content -import com.architect.coders.mu8.characters.CharactersUiModel.Loading -import com.architect.coders.mu8.characters.CharactersUiModel.Navigation -import com.architect.coders.mu8.characters.detail.CharactersDetailActivity -import com.architect.coders.mu8.data.DataApp -import com.architect.coders.mu8.data.characters.CharactersRepositoryImpl -import com.architect.coders.mu8.utils.getViewModel -import com.architect.coders.mu8.utils.startActivity -import com.architect.codes.mu8.characters.CharactersUseCaseImpl - -class CharactersActivity : AppCompatActivity() { - - private val toolbar: Toolbar by lazy { findViewById(R.id.toolbar) } - private val progress: ProgressBar by lazy { findViewById(R.id.progress) } - private val recycler: RecyclerView by lazy { findViewById(R.id.recycler) } - - private lateinit var viewModel: CharactersViewModel - private lateinit var adapter: CharactersAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_characters) - - setSupportActionBar(toolbar) - supportActionBar?.setDisplayShowTitleEnabled(false) - - val toolbarTitle = findViewById(R.id.toolbar_title) - toolbarTitle.text = getString(R.string.characters_name) - - viewModel = getViewModel { CharactersViewModel(CharactersUseCaseImpl(CharactersRepositoryImpl(application as DataApp))) } - - adapter = CharactersAdapter(viewModel::onCharacterClicked) - recycler.layoutManager = LinearLayoutManager(this) - recycler.adapter = adapter - - viewModel.model.observe(this, Observer(::updateUi)) - } - - private fun updateUi(model: CharactersUiModel) { - progress.visibility = if (model == Loading) VISIBLE else GONE - - when (model) { - is Content -> { - adapter.characters = model.characters - progress.visibility = GONE - } - is Navigation -> startActivity {} - } - } -} diff --git a/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersFragment.kt new file mode 100644 index 0000000..285e81e --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersFragment.kt @@ -0,0 +1,70 @@ +package com.architect.coders.mu8.characters + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.architect.coders.mu8.R +import com.architect.coders.mu8.common.NavigationFragment +import com.architect.coders.mu8.common.StartingNavHostActivity +import com.architect.coders.mu8.data.DataApp +import com.architect.coders.mu8.data.characters.CharactersRepositoryImpl +import com.architect.coders.mu8.utils.getViewModel +import com.architect.codes.mu8.characters.CharactersUseCaseImpl + +class CharactersFragment : NavigationFragment(R.id.action_charactersFragment_to_categoriesFragment) { + + private lateinit var recycler: RecyclerView + private lateinit var progress: ProgressBar + + private lateinit var viewModel: CharactersViewModel + private lateinit var adapter: CharactersAdapter + + private lateinit var navigationController: NavController + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_loader_list, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val navHostActivity = activity as StartingNavHostActivity + navHostActivity.setToolbarTitle(getString(R.string.characters_name)) + + navigationController = view.findNavController() + + viewModel = getViewModel { + CharactersViewModel(CharactersUseCaseImpl(CharactersRepositoryImpl(navHostActivity.application as DataApp))) + } + + adapter = CharactersAdapter(viewModel::onCharacterClicked) + + progress = view.findViewById(R.id.progress) + + recycler = view.findViewById(R.id.recycler) + recycler.layoutManager = LinearLayoutManager(context) + recycler.adapter = adapter + + viewModel.model.observe(this, Observer(::updateUi)) + } + + private fun updateUi(model: CharactersUiModel) { + val context = this.context ?: return + + progress.visibility = if (model == CharactersUiModel.Loading) View.VISIBLE else View.GONE + + when (model) { + is CharactersUiModel.Content -> { + adapter.characters = model.characters + progress.visibility = View.GONE + } + is CharactersUiModel.Navigation -> Toast.makeText(context, model.character.name, Toast.LENGTH_LONG).show() + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersViewModel.kt b/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersViewModel.kt index 82adfde..8f5b9bb 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersViewModel.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/characters/CharactersViewModel.kt @@ -2,7 +2,7 @@ package com.architect.coders.mu8.characters import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import com.architect.coders.mu8.utils.ScopedViewModel +import com.architect.coders.mu8.common.ScopedViewModel import com.architect.codes.mu8.characters.Character import com.architect.codes.mu8.characters.CharactersUseCase import kotlinx.coroutines.launch @@ -23,5 +23,7 @@ class CharactersViewModel(private val charactersUseCase: CharactersUseCase) : Sc } } - fun onCharacterClicked(character: Character) = Unit + fun onCharacterClicked(character: Character) { + _model.value = CharactersUiModel.Navigation(character) + } } diff --git a/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsActivity.kt deleted file mode 100644 index 445f328..0000000 --- a/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsActivity.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.architect.coders.mu8.comics - -import android.os.Bundle -import android.widget.ProgressBar -import android.widget.TextView -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.architect.coders.mu8.R -import com.architect.coders.mu8.comics.ComicsViewModel.UiModel.InternetError -import com.architect.coders.mu8.comics.ComicsViewModel.UiModel.LoadData -import com.architect.coders.mu8.comics.ComicsViewModel.UiModel.NavigateTo -import com.architect.coders.mu8.comics.ComicsViewModel.UiModel.ShowLoading -import com.architect.coders.mu8.data.comics.ComicRepositoryImpl -import com.architect.coders.mu8.data.comics.ComicsMapper -import com.architect.coders.mu8.utils.getViewModel -import com.architect.coders.mu8.utils.makeItGone -import com.architect.coders.mu8.utils.makeItVisible -import com.architect.codes.mu8.comics.ComicsUseCaseImpl -import com.google.android.material.snackbar.Snackbar - -class ComicsActivity : AppCompatActivity() { - - private val progress: ProgressBar by lazy { findViewById(R.id.comic_activity_progress) } - private val recycler: RecyclerView by lazy { findViewById(R.id.comic_activity_recycler) } - - private lateinit var toolbar: Toolbar - private lateinit var toolbarTitle: TextView - private lateinit var viewModel: ComicsViewModel - private lateinit var adapter: ComicsAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_comics) - - toolbar = findViewById(R.id.comic_activity_toolbar) - setSupportActionBar(toolbar) - supportActionBar?.setDisplayShowTitleEnabled(false) - - toolbarTitle = findViewById(R.id.toolbar_title) - toolbarTitle.text = getString(R.string.comics_name) - - viewModel = getViewModel { - ComicsViewModel( - ComicsUseCaseImpl( - ComicRepositoryImpl(ComicsMapper()) - ) - ) - } - - adapter = ComicsAdapter(viewModel::onComicClicked) - recycler.layoutManager = GridLayoutManager(this, GRID_COLUMS) - recycler.adapter = adapter - - viewModel.model.observe(this, Observer(::updateUi)) - } - - private fun updateUi(uiModel: ComicsViewModel.UiModel) { - return when (uiModel) { - is ShowLoading -> { - progress.makeItVisible() - recycler.makeItGone() - } - is LoadData -> { - adapter.comics = uiModel.comics - progress.makeItGone() - recycler.makeItVisible() - } - is InternetError -> { - progress.makeItGone() - recycler.makeItGone() - Snackbar.make( - recycler, - getString(R.string.error_internet_message), - Snackbar.LENGTH_LONG - ).show() - } - is NavigateTo -> Toast.makeText(this, uiModel.comic.title, Toast.LENGTH_LONG).show() - } - } - - companion object { - private const val GRID_COLUMS = 2 - } -} diff --git a/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsFragment.kt new file mode 100644 index 0000000..baae9e1 --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsFragment.kt @@ -0,0 +1,90 @@ +package com.architect.coders.mu8.comics + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.architect.coders.mu8.R +import com.architect.coders.mu8.common.NavigationFragment +import com.architect.coders.mu8.common.StartingNavHostActivity +import com.architect.coders.mu8.data.comics.ComicRepositoryImpl +import com.architect.coders.mu8.data.comics.ComicsMapper +import com.architect.coders.mu8.utils.getViewModel +import com.architect.coders.mu8.utils.makeItGone +import com.architect.coders.mu8.utils.makeItVisible +import com.architect.codes.mu8.comics.ComicsUseCaseImpl +import com.google.android.material.snackbar.Snackbar + +class ComicsFragment : NavigationFragment(R.id.action_comicsFragment_to_categoriesFragment) { + + private lateinit var recycler: RecyclerView + private lateinit var progress: ProgressBar + + private lateinit var viewModel: ComicsViewModel + private lateinit var adapter: ComicsAdapter + + private lateinit var navigationController: NavController + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_loader_list, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val navHostActivity = activity as StartingNavHostActivity + navHostActivity.showToolbar() + navHostActivity.setToolbarTitle(getString(R.string.comics_name)) + + navigationController = view.findNavController() + + viewModel = getViewModel { + ComicsViewModel( + ComicsUseCaseImpl( + ComicRepositoryImpl(ComicsMapper()) + ) + ) + } + + adapter = ComicsAdapter(viewModel::onComicClicked) + + progress = view.findViewById(R.id.progress) + + recycler = view.findViewById(R.id.recycler) + recycler.layoutManager = GridLayoutManager(context, GRID_COLUMNS) + recycler.adapter = adapter + + viewModel.model.observe(this, Observer(::updateUi)) + } + + private fun updateUi(uiModel: ComicsViewModel.UiModel) { + val context = this.context ?: return + + return when (uiModel) { + is ComicsViewModel.UiModel.ShowLoading -> { + progress.makeItVisible() + recycler.makeItGone() + } + is ComicsViewModel.UiModel.LoadData -> { + adapter.comics = uiModel.comics + progress.makeItGone() + recycler.makeItVisible() + } + is ComicsViewModel.UiModel.InternetError -> { + progress.makeItGone() + recycler.makeItGone() + Snackbar.make(recycler, getString(R.string.error_internet_message), Snackbar.LENGTH_LONG).show() + } + is ComicsViewModel.UiModel.NavigateTo -> Toast.makeText(context, uiModel.comic.title, Toast.LENGTH_LONG).show() + } + } + + companion object { + private const val GRID_COLUMNS = 2 + } +} diff --git a/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsViewModel.kt b/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsViewModel.kt index e076b76..45a2ef1 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsViewModel.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/comics/ComicsViewModel.kt @@ -3,7 +3,7 @@ package com.architect.coders.mu8.comics import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.architect.coders.mu8.comics.ComicsViewModel.UiModel.* -import com.architect.coders.mu8.utils.ScopedViewModel +import com.architect.coders.mu8.common.ScopedViewModel import com.architect.codes.mu8.comics.ComicUseCase import com.architect.codes.mu8.comics.Comic import kotlinx.coroutines.launch diff --git a/presentation/src/main/java/com/architect/coders/mu8/common/NavigationFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/common/NavigationFragment.kt new file mode 100644 index 0000000..ef77372 --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/common/NavigationFragment.kt @@ -0,0 +1,34 @@ +package com.architect.coders.mu8.common + +import android.os.Bundle +import androidx.activity.OnBackPressedCallback +import androidx.activity.addCallback +import androidx.annotation.CallSuper +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.NavHostFragment +import com.architect.codes.mu8.common.Scope + +abstract class NavigationFragment(private val action: Int? = null) : Fragment(), Scope by Scope.Implementation() { + + private lateinit var callback: OnBackPressedCallback + + @CallSuper + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + callback = requireActivity().onBackPressedDispatcher.addCallback(this, true) { onNavigateBack() } + } + + private fun onNavigateBack() { + if (action == null) { + activity?.finish() + return + } + NavHostFragment.findNavController(this).navigate(action) + } + + @CallSuper + override fun onDestroyView() { + super.onDestroyView() + callback.remove() + } +} diff --git a/presentation/src/main/java/com/architect/coders/mu8/utils/ScopedViewModel.kt b/presentation/src/main/java/com/architect/coders/mu8/common/ScopedViewModel.kt similarity index 89% rename from presentation/src/main/java/com/architect/coders/mu8/utils/ScopedViewModel.kt rename to presentation/src/main/java/com/architect/coders/mu8/common/ScopedViewModel.kt index 2f3b10d..c55f978 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/utils/ScopedViewModel.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/common/ScopedViewModel.kt @@ -1,4 +1,4 @@ -package com.architect.coders.mu8.utils +package com.architect.coders.mu8.common import androidx.annotation.CallSuper import androidx.lifecycle.ViewModel diff --git a/presentation/src/main/java/com/architect/coders/mu8/common/StartingNavHostActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/common/StartingNavHostActivity.kt new file mode 100644 index 0000000..b16dbbe --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/common/StartingNavHostActivity.kt @@ -0,0 +1,40 @@ +package com.architect.coders.mu8.common + +import android.os.Bundle +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import com.architect.coders.mu8.R + +class StartingNavHostActivity : AppCompatActivity() { + + private lateinit var toolbar: Toolbar + private lateinit var toolbarTitle: TextView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_nav_host) + + toolbar = findViewById(R.id.toolbar) + + setSupportActionBar(toolbar) + supportActionBar?.setDisplayShowTitleEnabled(false) + + toolbarTitle = toolbar.findViewById(R.id.toolbar_title) + toolbarTitle.text = getString(R.string.app_name) + } + + fun setToolbarTitle(title: String) { + if (::toolbarTitle.isInitialized) toolbarTitle.text = title + } + + fun hideToolbar() { + if (::toolbar.isInitialized) toolbar.visibility = GONE + } + + fun showToolbar() { + if (::toolbar.isInitialized) toolbar.visibility = VISIBLE + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/architect/coders/mu8/events/EventsActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/events/EventsActivity.kt deleted file mode 100644 index c9d8fb1..0000000 --- a/presentation/src/main/java/com/architect/coders/mu8/events/EventsActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.architect.coders.mu8.events - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.ProgressBar -import android.widget.TextView -import android.widget.Toast -import androidx.appcompat.widget.Toolbar -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.architect.coders.mu8.R -import com.architect.coders.mu8.data.events.EventsMapper -import com.architect.coders.mu8.data.events.EventsRepositoryImpl -import com.architect.coders.mu8.events.EventsUiModel.* -import com.architect.coders.mu8.utils.getViewModel -import com.architect.codes.mu8.events.EventsUserCaseImpl - -class EventsActivity : AppCompatActivity() { - - private val recyclerView: RecyclerView by lazy { findViewById(R.id.recycler) } - private val progress: ProgressBar by lazy { findViewById(R.id.progress) } - private val toolbar: Toolbar by lazy { findViewById(R.id.toolbar) } - - private lateinit var viewModel: EventsViewModel - private lateinit var adapter: EventsAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_events) - - setSupportActionBar(toolbar) - - val toolbarTitle = findViewById(R.id.toolbar_title) - toolbarTitle.text = getString(R.string.events_name) - - viewModel = getViewModel { EventsViewModel(EventsUserCaseImpl(EventsRepositoryImpl(EventsMapper()))) } - - adapter = EventsAdapter(viewModel::onEventClick) - recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.adapter = adapter - - viewModel.model.observe(this, Observer(::updateUi)) - } - - private fun updateUi(model: EventsUiModel) { - progress.visibility = if (model == Loading) VISIBLE else GONE - - when (model) { - is Contect -> adapter.events = model.events - is Navegation -> Toast.makeText(this, model.event.title, Toast.LENGTH_LONG).show() - } - } -} diff --git a/presentation/src/main/java/com/architect/coders/mu8/events/EventsFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/events/EventsFragment.kt new file mode 100644 index 0000000..39403aa --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/events/EventsFragment.kt @@ -0,0 +1,70 @@ +package com.architect.coders.mu8.events + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.architect.coders.mu8.R +import com.architect.coders.mu8.common.NavigationFragment +import com.architect.coders.mu8.common.StartingNavHostActivity +import com.architect.coders.mu8.data.events.EventsMapper +import com.architect.coders.mu8.data.events.EventsRepositoryImpl +import com.architect.coders.mu8.events.EventsUiModel.Contect +import com.architect.coders.mu8.events.EventsUiModel.Loading +import com.architect.coders.mu8.events.EventsUiModel.Navegation +import com.architect.coders.mu8.utils.getViewModel +import com.architect.codes.mu8.events.EventsUserCaseImpl + +class EventsFragment : NavigationFragment(R.id.action_eventsFragment_to_categoriesFragment) { + + private lateinit var recycler: RecyclerView + private lateinit var progress: ProgressBar + + private lateinit var viewModel: EventsViewModel + private lateinit var adapter: EventsAdapter + + private lateinit var navigationController: NavController + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_loader_list, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val navHostActivity = activity as StartingNavHostActivity + navHostActivity.setToolbarTitle(getString(R.string.events_name)) + + navigationController = view.findNavController() + + viewModel = getViewModel { EventsViewModel(EventsUserCaseImpl(EventsRepositoryImpl(EventsMapper()))) } + + adapter = EventsAdapter(viewModel::onEventClick) + + progress = view.findViewById(R.id.progress) + + recycler = view.findViewById(R.id.recycler) + recycler.layoutManager = LinearLayoutManager(context) + recycler.adapter = adapter + + viewModel.model.observe(this, Observer(::updateUi)) + } + + private fun updateUi(model: EventsUiModel) { + val context = this.context ?: return + + progress.visibility = if (model == Loading) VISIBLE else GONE + + when (model) { + is Contect -> adapter.events = model.events + is Navegation -> Toast.makeText(context, model.event.title, Toast.LENGTH_LONG).show() + } + } +} diff --git a/presentation/src/main/java/com/architect/coders/mu8/events/EventsViewModel.kt b/presentation/src/main/java/com/architect/coders/mu8/events/EventsViewModel.kt index 8b0af77..677d577 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/events/EventsViewModel.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/events/EventsViewModel.kt @@ -3,7 +3,7 @@ package com.architect.coders.mu8.events import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.architect.coders.mu8.events.EventsUiModel.Navegation -import com.architect.coders.mu8.utils.ScopedViewModel +import com.architect.coders.mu8.common.ScopedViewModel import com.architect.codes.mu8.events.Event import com.architect.codes.mu8.events.EventsUseCase import kotlinx.coroutines.launch diff --git a/presentation/src/main/java/com/architect/coders/mu8/splash/SplashActivity.kt b/presentation/src/main/java/com/architect/coders/mu8/splash/SplashActivity.kt deleted file mode 100644 index 1a133af..0000000 --- a/presentation/src/main/java/com/architect/coders/mu8/splash/SplashActivity.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.architect.coders.mu8.splash - -import android.os.Bundle -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.Observer -import com.architect.coders.mu8.R -import com.architect.coders.mu8.categories.CategoriesActivity -import com.architect.coders.mu8.splash.SplashViewModel.UiModel -import com.architect.coders.mu8.utils.getViewModel -import com.architect.coders.mu8.utils.startActivity - -class SplashActivity : AppCompatActivity() { - - private lateinit var viewModel: SplashViewModel - private lateinit var tvVersion: TextView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_splash) - - tvVersion = findViewById(R.id.tv_version) - viewModel = getViewModel { SplashViewModel() } - - viewModel.model.observe(this, Observer(::initUi)) - } - - private fun initUi(model: UiModel) { - when (model) { - is UiModel.GetVersion -> tvVersion.text = getString(R.string.version_name, model.versionName) - is UiModel.Navigation -> { - startActivity {} - finish() - } - } - } -} diff --git a/presentation/src/main/java/com/architect/coders/mu8/splash/SplashFragment.kt b/presentation/src/main/java/com/architect/coders/mu8/splash/SplashFragment.kt new file mode 100644 index 0000000..94c9f7f --- /dev/null +++ b/presentation/src/main/java/com/architect/coders/mu8/splash/SplashFragment.kt @@ -0,0 +1,44 @@ +package com.architect.coders.mu8.splash + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.navigation.NavController +import androidx.navigation.findNavController +import com.architect.coders.mu8.R +import com.architect.coders.mu8.common.StartingNavHostActivity +import com.architect.coders.mu8.utils.getViewModel + +class SplashFragment : Fragment() { + + private lateinit var viewModel: SplashViewModel + private lateinit var appVersion: TextView + + private lateinit var navController: NavController + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_splash, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + (activity as StartingNavHostActivity).hideToolbar() + + navController = view.findNavController() + + appVersion = view.findViewById(R.id.tv_version) + viewModel = getViewModel { SplashViewModel() } + + viewModel.model.observe(this, Observer(::initUi)) + } + + private fun initUi(model: SplashViewModel.UiModel) { + when (model) { + is SplashViewModel.UiModel.GetVersion -> appVersion.text = getString(R.string.version_name, model.versionName) + is SplashViewModel.UiModel.Navigation -> navController.navigate(R.id.action_splashFragment_to_categoriesFragment) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/architect/coders/mu8/splash/SplashViewModel.kt b/presentation/src/main/java/com/architect/coders/mu8/splash/SplashViewModel.kt index 7f4c4d0..e29e8f4 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/splash/SplashViewModel.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/splash/SplashViewModel.kt @@ -3,7 +3,7 @@ package com.architect.coders.mu8.splash import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.architect.coders.mu8.BuildConfig -import com.architect.coders.mu8.utils.ScopedViewModel +import com.architect.coders.mu8.common.ScopedViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/presentation/src/main/java/com/architect/coders/mu8/utils/PresentationExtensions.kt b/presentation/src/main/java/com/architect/coders/mu8/utils/PresentationExtensions.kt index 155cd1c..c949264 100644 --- a/presentation/src/main/java/com/architect/coders/mu8/utils/PresentationExtensions.kt +++ b/presentation/src/main/java/com/architect/coders/mu8/utils/PresentationExtensions.kt @@ -1,3 +1,5 @@ +@file:Suppress("UNCHECKED_CAST") + package com.architect.coders.mu8.utils import android.app.Activity @@ -8,6 +10,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -57,3 +60,10 @@ inline fun FragmentActivity.getViewModel(crossinline fac } return ViewModelProviders.of(this, vmFactory)[T::class.java] } + +inline fun Fragment.getViewModel(crossinline factory: () -> T): T { + val vmFactory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): U = factory() as U + } + return ViewModelProviders.of(this, vmFactory)[T::class.java] +} diff --git a/presentation/src/main/res/layout/activity_categories.xml b/presentation/src/main/res/layout/activity_categories.xml deleted file mode 100644 index 183c5a8..0000000 --- a/presentation/src/main/res/layout/activity_categories.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_comics.xml b/presentation/src/main/res/layout/activity_comics.xml deleted file mode 100644 index 4f290ed..0000000 --- a/presentation/src/main/res/layout/activity_comics.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_events.xml b/presentation/src/main/res/layout/activity_events.xml deleted file mode 100644 index 684e0a2..0000000 --- a/presentation/src/main/res/layout/activity_events.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_nav_host.xml b/presentation/src/main/res/layout/activity_nav_host.xml new file mode 100644 index 0000000..11c9ce8 --- /dev/null +++ b/presentation/src/main/res/layout/activity_nav_host.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_list.xml b/presentation/src/main/res/layout/fragment_list.xml new file mode 100644 index 0000000..d110909 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_list.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_characters.xml b/presentation/src/main/res/layout/fragment_loader_list.xml similarity index 84% rename from presentation/src/main/res/layout/activity_characters.xml rename to presentation/src/main/res/layout/fragment_loader_list.xml index 640c9a6..efd83de 100644 --- a/presentation/src/main/res/layout/activity_characters.xml +++ b/presentation/src/main/res/layout/fragment_loader_list.xml @@ -6,10 +6,6 @@ android:layout_height="match_parent" tools:context=".categories.CategoriesActivity"> - - + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_splash.xml b/presentation/src/main/res/layout/fragment_splash.xml similarity index 81% rename from presentation/src/main/res/layout/activity_splash.xml rename to presentation/src/main/res/layout/fragment_splash.xml index da492a5..18b1e61 100644 --- a/presentation/src/main/res/layout/activity_splash.xml +++ b/presentation/src/main/res/layout/fragment_splash.xml @@ -1,11 +1,9 @@ + android:background="@color/colorPrimary"> + app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index a29c35b..c1f46da 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -6,5 +6,5 @@ Characters Comics - Eventos + Events