From 0c3577d4a1b53d45f2a20653ce7af7a4f98837a3 Mon Sep 17 00:00:00 2001 From: Reski Date: Fri, 12 Nov 2021 21:47:21 +0700 Subject: [PATCH] refactoring code base. --- .../com/grevi/masakapa/ui/HomeActivity.kt | 82 ++---------- .../ui/adapter/CategoryItemAdapter.kt | 8 +- .../grevi/masakapa/ui/base/BaseActivity.kt | 106 ++++++++++++++- .../grevi/masakapa/ui/base/BaseFragment.kt | 11 +- .../grevi/masakapa/ui/base/BaseFragmentExt.kt | 17 ++- .../masakapa/ui/category/CategoryFragment.kt | 124 ++++++------------ .../masakapa/ui/recipes/RecipesFragment.kt | 3 +- .../masakapa/ui/splash/SplashActivity.kt | 12 +- 8 files changed, 192 insertions(+), 171 deletions(-) diff --git a/app/src/main/java/com/grevi/masakapa/ui/HomeActivity.kt b/app/src/main/java/com/grevi/masakapa/ui/HomeActivity.kt index 0f0df5f..9025272 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/HomeActivity.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/HomeActivity.kt @@ -1,90 +1,36 @@ package com.grevi.masakapa.ui -import android.Manifest -import android.content.Context import android.os.Bundle -import android.view.Menu -import android.view.MenuItem +import android.view.LayoutInflater import android.view.View -import androidx.appcompat.app.AppCompatDelegate -import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.NavController import com.grevi.masakapa.R import com.grevi.masakapa.databinding.ActivityHomeBinding import com.grevi.masakapa.ui.base.BaseActivity -import com.grevi.masakapa.util.Constant.PERMISSIONS_STORAGE -import com.grevi.masakapa.util.snackBar -import com.permissionx.guolindev.PermissionX +import dagger.hilt.android.AndroidEntryPoint -class HomeActivity : BaseActivity() { - private lateinit var binding : ActivityHomeBinding - private var state = false +@AndroidEntryPoint +class HomeActivity : BaseActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityHomeBinding.inflate(layoutInflater) - setContentView(binding.root) + override fun subscribeUI() { setSupportActionBar(binding.toolbar) supportActionBar?.setIcon(R.drawable.ic_icon_text) supportActionBar?.title = null - - //init navhost container - val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_container) as NavHostFragment - navHostFragment.navController - navHostFragment.navController.addOnDestinationChangedListener { controller, destination, _ -> - when(destination.id) { - R.id.recipesFragment -> { - binding.searchCard.visibility = View.VISIBLE - binding.searchCard.setOnClickListener { - controller.navigate(R.id.searchFragment2) - } - } - else -> binding.searchCard.visibility = View.GONE - } - } - storageHandler() } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - super.onCreateOptionsMenu(menu) - menuInflater.inflate(R.menu.toolbar_menu, menu) - return true + override fun getViewBindingInflater(inflater: LayoutInflater): ActivityHomeBinding { + return ActivityHomeBinding.inflate(inflater) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when(item.itemId) { - R.id.dayNight -> { - state = when(state) { - true -> { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - false - } - else -> { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - true - } - } - } + override fun navigationStateView(nav: NavController) = with(binding) { + searchCard.visibility = View.VISIBLE + searchCard.setOnClickListener { + nav.navigate(R.id.searchFragment2) } - //return true - return super.onOptionsItemSelected(item) } - private fun storageHandler() { - PermissionX.init(this) - .permissions(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) - .onExplainRequestReason { scope, deniedList, _ -> - scope.showRequestReasonDialog(deniedList, getString(R.string.bucket_permission_reason), "Ok", "Cancel") - } - .request { allGranted, _, _ -> - if (allGranted) { - this.getSharedPreferences(PERMISSIONS_STORAGE, Context.MODE_PRIVATE).apply { - edit().let { - it.putBoolean(PERMISSIONS_STORAGE, true) - it.apply() - } - } - } - } + override fun navigationOnDisableView() = with(binding) { + searchCard.visibility = View.GONE } } \ No newline at end of file diff --git a/app/src/main/java/com/grevi/masakapa/ui/adapter/CategoryItemAdapter.kt b/app/src/main/java/com/grevi/masakapa/ui/adapter/CategoryItemAdapter.kt index abb1038..6af9e25 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/adapter/CategoryItemAdapter.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/adapter/CategoryItemAdapter.kt @@ -10,11 +10,11 @@ import com.grevi.masakapa.databinding.ListsRecipesBinding import com.grevi.masakapa.model.Recipes import com.grevi.masakapa.util.DiffUtils -class CategoryItemAdapter : RecyclerView.Adapter() { +class CategoryItemAdapter(private val itemTouch : ((recipes : Recipes) -> Unit)) + : RecyclerView.Adapter() { private val recipes : MutableList = mutableListOf() - internal var itemTouch : ((recipes : Recipes) -> Unit)? = null - inner class CategoryItemVH(private val binding : ListsRecipesBinding) : RecyclerView.ViewHolder(binding.root) { + class CategoryItemVH(private val binding : ListsRecipesBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(recipes : Recipes) { with(binding) { imgThumb.load(recipes.imageThumb) { @@ -49,6 +49,6 @@ class CategoryItemAdapter : RecyclerView.Adapter : AppCompatActivity() { + + private lateinit var _binding : VB + protected val binding get() = _binding + + private var state = false //should save state on shared pref + + abstract fun getViewBindingInflater(inflater: LayoutInflater): VB + + abstract fun navigationStateView(nav: NavController) + + abstract fun navigationOnDisableView() + + abstract fun subscribeUI() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + _binding = getViewBindingInflater(layoutInflater) + setContentView(_binding.root) supportActionBar?.hide() supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.WHITE)) + subscribeUI() + storageHandler() + setupNavigation(onBindView = { + navigationStateView(it) + }, onNegativeView = { + navigationOnDisableView() + }) + } + + private fun setupNavigation( + onBindView : (nav :NavController) -> Unit, + onNegativeView: () -> Unit + ) { + val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_container) as NavHostFragment + navHostFragment.navController.addOnDestinationChangedListener { controller, destination, _ -> + when(destination.id) { + R.id.recipesFragment -> onBindView(controller) + else -> onNegativeView() + } + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + super.onCreateOptionsMenu(menu) + menuInflater.inflate(R.menu.toolbar_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when(item.itemId) { + R.id.dayNight -> { + state = when(state) { + true -> { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + false + } + else -> { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + true + } + } + } + } + //return true + return super.onOptionsItemSelected(item) + } + + private fun storageHandler() { + PermissionX.init(this) + .permissions( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE) + .onExplainRequestReason { scope, deniedList, _ -> + scope.showRequestReasonDialog(deniedList, + getString(R.string.bucket_permission_reason), + "Ok", + "Cancel") + } + .request { allGranted, _, _ -> + if (allGranted) { + this.getSharedPreferences(Constant.PERMISSIONS_STORAGE, Context.MODE_PRIVATE) + .apply { + edit().let { + it.putBoolean(Constant.PERMISSIONS_STORAGE, true) + it.apply() + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragment.kt b/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragment.kt index de9301f..b466419 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragment.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragment.kt @@ -67,15 +67,14 @@ abstract class BaseFragment : Fragment() { } protected fun onSwipeRefresh( - view: SwipeRefreshLayout, - pg : LinearProgressIndicator, - action: () -> Unit + view: SwipeRefreshLayout?, + pg : LinearProgressIndicator?, ) { networkUtils.networkDataStatus.observe(viewLifecycleOwner, { - view.setOnRefreshListener { + view?.setOnRefreshListener { Handler(Looper.getMainLooper()).postDelayed({ - action.invoke() - pg.visibility = View.GONE + subscribeUI() + pg?.visibility = View.GONE }, TWO_SECOND) } }) diff --git a/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragmentExt.kt b/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragmentExt.kt index 55d18c6..22d1ef0 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragmentExt.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/base/BaseFragmentExt.kt @@ -1,5 +1,6 @@ package com.grevi.masakapa.ui.base +import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope import com.grevi.masakapa.util.State import com.grevi.masakapa.util.snackBar @@ -7,7 +8,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import java.lang.IllegalStateException -fun BaseFragment<*,*>.observeDataListFlow(data: MutableStateFlow>, onSuccessState: (T) -> Unit) { +fun BaseFragment<*,*>.observeDataListFlow( + data: MutableStateFlow>, + onSuccessState: (T) -> Unit +) { lifecycleScope.launchWhenCreated { data.collect { when(it) { @@ -18,4 +22,15 @@ fun BaseFragment<*,*>.observeDataListFlow(data: MutableStateFlow>, o } } } +} + +fun BaseFragment<*,*>.observeLiveData(data: LiveData>, onSuccessState: (T) -> Unit) { + data.observe(viewLifecycleOwner, { + when(it) { + is State.Error -> snackBar(getBinding().root, it.msg).show() + is State.Loading -> snackBar(getBinding().root, it.msg).show() + is State.Success -> onSuccessState(it.data) + else -> throw IllegalStateException("State live datanya ngaco bang") + } + }) } \ No newline at end of file diff --git a/app/src/main/java/com/grevi/masakapa/ui/category/CategoryFragment.kt b/app/src/main/java/com/grevi/masakapa/ui/category/CategoryFragment.kt index 2697152..233fce4 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/category/CategoryFragment.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/category/CategoryFragment.kt @@ -1,55 +1,43 @@ package com.grevi.masakapa.ui.category -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.util.Log -import android.view.* -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.navigation.NavController -import androidx.navigation.Navigation +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.ViewGroup import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.grevi.masakapa.R import com.grevi.masakapa.databinding.FragmentCategoryBinding import com.grevi.masakapa.model.Recipes import com.grevi.masakapa.ui.adapter.CategoryItemAdapter +import com.grevi.masakapa.ui.base.BaseFragment +import com.grevi.masakapa.ui.base.observeLiveData import com.grevi.masakapa.ui.viewmodel.RecipesViewModel -import com.grevi.masakapa.util.NetworkUtils -import com.grevi.masakapa.util.State -import com.grevi.masakapa.util.snackBar -import com.grevi.masakapa.util.toast +import com.grevi.masakapa.util.Constant.ONE_FLOAT +import com.grevi.masakapa.util.Constant.ONE_SECOND +import com.grevi.masakapa.util.Constant.ZERO_FLOAT import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class CategoryFragment : Fragment() { +class CategoryFragment : BaseFragment() { - private lateinit var binding : FragmentCategoryBinding private val arg : CategoryFragmentArgs by navArgs() - private val recipesViewModel by viewModels() - private val categoryItemAdapter: CategoryItemAdapter by lazy { CategoryItemAdapter() } - private lateinit var navController: NavController - private val networkUtils : NetworkUtils by lazy { NetworkUtils(requireContext()) } + private val categoryItemAdapter: CategoryItemAdapter by lazy { + CategoryItemAdapter { navigateToDetail(it) } + } - private val TAG = CategoryFragment::class.java.simpleName + override fun getViewModelClass(): Class = RecipesViewModel::class.java - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - binding = FragmentCategoryBinding.inflate(inflater) - return binding.root + override fun getViewBindingInflater( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentCategoryBinding { + return FragmentCategoryBinding.inflate(inflater, container, false) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setHasOptionsMenu(true) - Log.v(TAG, arg.catKey) - navController = Navigation.findNavController(view) - observeNetwork() - swipeRefresh() + override fun subscribeUI() { + observeRecipes() + binding.apply { onSwipeRefresh(refreshCatLayout, null) } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -57,60 +45,30 @@ class CategoryFragment : Fragment() { menu.findItem(R.id.bucket)?.isVisible = false } - private fun prepareView() = with(binding) { - rvRecipesCategoryList.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) - rvRecipesCategoryList.adapter = categoryItemAdapter - refreshCatLayout.isRefreshing = true - - rvRecipesCategoryList.animate().alpha(0f).duration = 1000L - - recipesViewModel.categoryResult(arg.catKey).observe( - viewLifecycleOwner, { results -> - categoryHintText.text = "${arg.catName} (0)" - when (results) { - is State.Loading -> Log.i(TAG, results.msg) - is State.Error -> { - toast(requireContext(), results.msg) - refreshCatLayout.isRefreshing = true - } - is State.Success -> { - results.data.results.let { categoryItemAdapter.addItem(it) } - rvRecipesCategoryList.animate().alpha(1f).duration = 1000L - refreshCatLayout.isRefreshing = false - categoryHintText.text = "${arg.catName} (${results.data.results?.size})" - } - else -> Log.i(TAG, "") - } - }) - categoryItemAdapter.itemTouch = { prepareNavigate(it) } - } - - private fun swipeRefresh() = with(binding) { - networkUtils.networkDataStatus.observe(viewLifecycleOwner) { isConnect -> - if (isConnect) { - refreshCatLayout.setOnRefreshListener { - Handler(Looper.getMainLooper()).postDelayed({ - prepareView() - }, 2000L) - } - } else { - snackBar(root, getString(R.string.no_inet_text)).show() - } + private fun observeRecipes() = with(viewModels) { + observeLiveData(categoryResult(arg.catKey)) { recipes -> + observeViewState(recipes.results) } } - private fun observeNetwork() { - networkUtils.networkDataStatus.observe(viewLifecycleOwner) { isConnect -> - if (isConnect) { - prepareView() - } else { - snackBar(binding.root, getString(R.string.no_inet_text)).show() - } - } + private fun observeViewState(recipes: MutableList) = with(binding) { + rvRecipesCategoryList.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.VERTICAL, + false) + rvRecipesCategoryList.adapter = categoryItemAdapter + refreshCatLayout.isRefreshing = true + rvRecipesCategoryList.animate().alpha(ZERO_FLOAT).duration = ONE_SECOND + + categoryItemAdapter.addItem(recipes) + rvRecipesCategoryList.animate().alpha(ONE_FLOAT).duration = ONE_SECOND + refreshCatLayout.isRefreshing = false + categoryHintText.text = "${arg.catName} (${recipes.size})" } - private fun prepareNavigate(recipes: Recipes) { - CategoryFragmentDirections.actionCategoryFragment2ToDetailFragment(recipes.key, recipes.imageThumb).also { + private fun navigateToDetail(recipes: Recipes) { + CategoryFragmentDirections + .actionCategoryFragment2ToDetailFragment(recipes.key, recipes.imageThumb).also { navController.navigate(it) } } diff --git a/app/src/main/java/com/grevi/masakapa/ui/recipes/RecipesFragment.kt b/app/src/main/java/com/grevi/masakapa/ui/recipes/RecipesFragment.kt index 9a4a2e0..c0040db 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/recipes/RecipesFragment.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/recipes/RecipesFragment.kt @@ -1,6 +1,5 @@ package com.grevi.masakapa.ui.recipes -import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem import android.view.View @@ -52,7 +51,7 @@ class RecipesFragment : BaseFragment() observeView() observeRecipes() binding.apply { - onSwipeRefresh(refreshLayout, pg) { observeView() } + onSwipeRefresh(refreshLayout, pg) } } diff --git a/app/src/main/java/com/grevi/masakapa/ui/splash/SplashActivity.kt b/app/src/main/java/com/grevi/masakapa/ui/splash/SplashActivity.kt index 805e4f9..51cb282 100644 --- a/app/src/main/java/com/grevi/masakapa/ui/splash/SplashActivity.kt +++ b/app/src/main/java/com/grevi/masakapa/ui/splash/SplashActivity.kt @@ -1,23 +1,29 @@ package com.grevi.masakapa.ui.splash import android.content.Intent +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.os.Handler import android.os.Looper +import androidx.appcompat.app.AppCompatActivity import com.grevi.masakapa.R import com.grevi.masakapa.ui.HomeActivity import com.grevi.masakapa.ui.base.BaseActivity +import com.grevi.masakapa.util.Constant.TWO_SECOND +import dagger.hilt.android.AndroidEntryPoint -class SplashActivity : BaseActivity() { +@AndroidEntryPoint +class SplashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - + supportActionBar?.hide() Handler(Looper.getMainLooper()).postDelayed({ Intent(this, HomeActivity::class.java).apply { startActivity(this) finish() } - }, 2000L) + }, TWO_SECOND) } } \ No newline at end of file