From 09be3e83ab754c5f35c44ba12691d73c54e3d6a5 Mon Sep 17 00:00:00 2001 From: amirisback Date: Sun, 21 Jan 2024 16:55:08 +0700 Subject: [PATCH] add: ui --- .../common/base/BaseActivity.kt | 5 +- .../common/base/BaseFragment.kt | 56 +++++++++ .../androidresearch/common/ext/GsonExt.kt | 4 + .../common/ext/ImageViewExt.kt | 53 ++++++++ .../androidresearch/model/MainModel.kt | 25 ++++ .../ui/detail/DetailActivity.kt | 5 +- .../androidresearch/ui/main/MainActivity.kt | 77 +++++------- .../androidresearch/ui/main/MainAdapter.kt | 41 ++++++ .../androidresearch/ui/main/MainFragment.kt | 117 ++++++++++++++++++ .../androidresearch/ui/main/MainHolder.kt | 28 +++++ .../androidresearch/ui/main/MainMapper.kt | 38 ++++++ .../androidresearch/ui/main/MainMenuType.kt | 15 +++ app/src/main/res/layout/activity_main.xml | 17 ++- app/src/main/res/layout/fragment_main.xml | 20 +++ app/src/main/res/layout/item_main.xml | 39 ++++++ .../res/menu/bottom_navigation_home_menu.xml | 11 ++ 16 files changed, 494 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseFragment.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/ImageViewExt.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/model/MainModel.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainAdapter.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainFragment.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainHolder.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMapper.kt create mode 100644 app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMenuType.kt create mode 100644 app/src/main/res/layout/fragment_main.xml create mode 100644 app/src/main/res/layout/item_main.xml create mode 100644 app/src/main/res/menu/bottom_navigation_home_menu.xml diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseActivity.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseActivity.kt index 0de7f7f..4b0824b 100644 --- a/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseActivity.kt +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseActivity.kt @@ -18,15 +18,16 @@ abstract class BaseActivity : AppCompatActivity() { abstract fun setupViewBinding(): VB + abstract fun initUI(savedInstanceState: Bundle?) + abstract fun initObserver() - abstract fun initUI() protected val binding by lazy { setupViewBinding() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) - initUI() + initUI(savedInstanceState) initObserver() } diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseFragment.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseFragment.kt new file mode 100644 index 0000000..c399ee4 --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/common/base/BaseFragment.kt @@ -0,0 +1,56 @@ +package com.qomunal.opensource.androidresearch.common.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +abstract class BaseFragment : Fragment() { + + companion object { + val TAG: String = BaseFragment::class.java.simpleName + } + + private var _binding: VB? = null + + protected val binding: VB get() = _binding!! + + abstract fun setupViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB + + abstract fun initUI(savedInstanceState: Bundle?) + + abstract fun initObserver() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = setupViewBinding(inflater, container) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initUI(savedInstanceState) + initObserver() + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/GsonExt.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/GsonExt.kt index 16c418d..cd2b66a 100644 --- a/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/GsonExt.kt +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/GsonExt.kt @@ -15,4 +15,8 @@ import com.google.gson.reflect.TypeToken inline fun fromJson(json: String?): T { return Gson().fromJson(json, object : TypeToken() {}.type) +} + +inline fun T.toJson(): String { + return Gson().toJson(this) } \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/ImageViewExt.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/ImageViewExt.kt new file mode 100644 index 0000000..b389df0 --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/common/ext/ImageViewExt.kt @@ -0,0 +1,53 @@ +package com.qomunal.opensource.androidresearch.common.ext + +import android.net.Uri +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.qomunal.opensource.androidresearch.R + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +fun ImageView.setImageExt( + uri: Any? = null, + placeHolder: Int = R.drawable.ic_launcher_background, + isCenterCrop: Boolean = false +) { + if (uri != null) { + when (uri) { + is String, + is Int, + is Uri, + is ByteArray -> { + if (isCenterCrop) { + Glide.with(context) + .load(uri) + .placeholder(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .centerCrop() + .into(this) + } else { + Glide.with(context) + .load(uri) + .placeholder(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(this) + } + + } + } + } else { + Glide.with(context) + .load("") + .placeholder(placeHolder) + .into(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/model/MainModel.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/model/MainModel.kt new file mode 100644 index 0000000..1ae89d1 --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/model/MainModel.kt @@ -0,0 +1,25 @@ +package com.qomunal.opensource.androidresearch.model + +import android.os.Parcelable +import androidx.annotation.Keep +import kotlinx.parcelize.Parcelize + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +@Keep +@Parcelize +data class MainModel( + var title: String, + var description: String, + var image: String, + var data: String, +) : Parcelable + diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/detail/DetailActivity.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/detail/DetailActivity.kt index 784a366..6bac6ca 100644 --- a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/detail/DetailActivity.kt +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/detail/DetailActivity.kt @@ -28,11 +28,8 @@ class DetailActivity : BaseActivity() { return ActivityDetailBinding.inflate(layoutInflater) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } - override fun initUI() { + override fun initUI(savedInstanceState: Bundle?) { binding.apply { } diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainActivity.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainActivity.kt index d3048b3..3416ac4 100644 --- a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainActivity.kt +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainActivity.kt @@ -1,11 +1,13 @@ package com.qomunal.opensource.androidresearch.ui.main import android.os.Bundle -import android.util.Log import androidx.activity.viewModels +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import com.qomunal.opensource.androidresearch.R import com.qomunal.opensource.androidresearch.common.base.BaseActivity import com.qomunal.opensource.androidresearch.databinding.ActivityMainBinding -import com.qomunal.opensource.androidresearch.domain.Resource import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -20,66 +22,47 @@ class MainActivity : BaseActivity() { return ActivityMainBinding.inflate(layoutInflater) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun initUI(savedInstanceState: Bundle?) { if (savedInstanceState == null) { - viewModel.getNewsArticle() - viewModel.getMeals() + replaceFragment(MainFragment.newInstance(MainMenuType.NEWS)) } - } - override fun initUI() { binding.apply { + bottomNav.setOnItemSelectedListener { + when (it.itemId) { - } - } - - override fun initObserver() { - viewModel.apply { - newsArticlesState.observe(this@MainActivity) { - when (it) { - is Resource.Error -> { - Log.d("MainActivity", "Error") + R.id.nv_news -> { + replaceFragment(MainFragment.newInstance(MainMenuType.NEWS)) + return@setOnItemSelectedListener true } - is Resource.Loading -> { - Log.d("MainActivity", "Loading") + R.id.nv_meal -> { + replaceFragment(MainFragment.newInstance(MainMenuType.MEAL)) + return@setOnItemSelectedListener true } - is Resource.Success -> { - Log.d("MainActivity", "Success") - Log.d("MainActivity", it.data.toString()) - } } + return@setOnItemSelectedListener false } - mealState.observe(this@MainActivity) { - when (it) { - is Resource.Error -> { - Log.d("MainActivity", "Error") - } - - is Resource.Loading -> { - Log.d("MainActivity", "Loading") - } - - is Resource.Success -> { - Log.d("MainActivity", "Success") - Log.d("MainActivity", it.data.toString()) - - it.data?.forEach { meal -> + } + } - Log.d("Meal Name", meal.strMeal) + override fun initObserver() { + viewModel.apply { - meal.strIngredient.forEach { ingredient -> - Log.d("Meal Ingredient", ingredient) - } + } + } - Log.d("Meal Space", "------------------------------") - } - } - } - } + private fun replaceFragment(fragment: Fragment) { + val backStateName: String = fragment::class.java.name + val manager: FragmentManager = supportFragmentManager + val fragmentPopped: Boolean = manager.popBackStackImmediate(backStateName, 0) + if (!fragmentPopped) { //fragment not in back stack, create it. + val ft: FragmentTransaction = manager.beginTransaction() + ft.replace(binding.fragmentMain.id, fragment) + ft.addToBackStack(backStateName) + ft.commit() } } diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainAdapter.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainAdapter.kt new file mode 100644 index 0000000..7799f2f --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainAdapter.kt @@ -0,0 +1,41 @@ +package com.qomunal.opensource.androidresearch.ui.main + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.qomunal.opensource.androidresearch.databinding.ItemMainBinding +import com.qomunal.opensource.androidresearch.model.MainModel + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +class MainAdapter : RecyclerView.Adapter() { + + var listItems = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHolder { + val binding = ItemMainBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return MainHolder(binding) + } + + override fun getItemCount(): Int { + return listItems.size + } + + override fun onBindViewHolder(holder: MainHolder, position: Int) { + holder.bind(listItems[position]) + } + + fun setItem(item: MutableList) { + listItems = item + notifyDataSetChanged() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainFragment.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainFragment.kt new file mode 100644 index 0000000..622f8eb --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainFragment.kt @@ -0,0 +1,117 @@ +package com.qomunal.opensource.androidresearch.ui.main + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.qomunal.opensource.androidresearch.common.base.BaseFragment +import com.qomunal.opensource.androidresearch.databinding.FragmentMainBinding +import com.qomunal.opensource.androidresearch.domain.Resource +import dagger.hilt.android.AndroidEntryPoint + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + +@AndroidEntryPoint +class MainFragment : BaseFragment() { + + companion object { + + const val EXTRA_MENU = "EXTRA_MENU" + + fun newInstance(menu: MainMenuType): MainFragment { + return MainFragment().apply { + arguments = Bundle().apply { + putString(EXTRA_MENU, menu.name) + } + + } + } + } + + private val viewModel: MainViewModel by activityViewModels() + + private var mainAdapter = MainAdapter() + + override fun setupViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentMainBinding { + return FragmentMainBinding.inflate(inflater, container, false) + } + + override fun initUI(savedInstanceState: Bundle?) { + val menu = MainMenuType.valueOf(arguments?.getString(EXTRA_MENU) ?: "") + + if (savedInstanceState == null) { + when (menu) { + MainMenuType.NEWS -> { + viewModel.getNewsArticle() + } + + MainMenuType.MEAL -> { + viewModel.getMeals() + } + } + } + + binding.apply { + + rvMain.apply { + val layout = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) + adapter = mainAdapter + layoutManager = layout + } + } + } + + override fun initObserver() { + viewModel.apply { + newsArticlesState.observe(viewLifecycleOwner) { + when (it) { + is Resource.Error -> { + + } + + is Resource.Loading -> { + + } + + is Resource.Success -> { + val data = it.data?.map { setData -> + MainMapper.articleMapper(setData) + }?.toMutableList() ?: mutableListOf() + mainAdapter.setItem(data) + } + } + } + + mealState.observe(viewLifecycleOwner) { + when (it) { + is Resource.Error -> { + + } + + is Resource.Loading -> { + + } + + is Resource.Success -> { + val data = it.data?.map { setData -> + MainMapper.mealMapper(setData) + }?.toMutableList() ?: mutableListOf() + mainAdapter.setItem(data) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainHolder.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainHolder.kt new file mode 100644 index 0000000..f1a0708 --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainHolder.kt @@ -0,0 +1,28 @@ +package com.qomunal.opensource.androidresearch.ui.main + +import androidx.recyclerview.widget.RecyclerView +import com.qomunal.opensource.androidresearch.common.ext.setImageExt +import com.qomunal.opensource.androidresearch.databinding.ItemMainBinding +import com.qomunal.opensource.androidresearch.model.MainModel + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +class MainHolder(private val binding: ItemMainBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(data : MainModel) { + binding.apply { + tvTitle.text = data.title + tvDesc.text = data.description + ivImage.setImageExt(data.image) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMapper.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMapper.kt new file mode 100644 index 0000000..346f01d --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMapper.kt @@ -0,0 +1,38 @@ +package com.qomunal.opensource.androidresearch.ui.main + +import com.qomunal.opensource.androidresearch.common.ext.toJson +import com.qomunal.opensource.androidresearch.model.ArticleModel +import com.qomunal.opensource.androidresearch.model.MainModel +import com.qomunal.opensource.androidresearch.model.MealModel + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +object MainMapper { + + fun articleMapper(input: ArticleModel): MainModel { + return MainModel( + title = input.title, + description = input.description, + image = input.urlToImage, + data = input.toJson(), + ) + } + + fun mealMapper(input: MealModel): MainModel { + return MainModel( + title = input.strMeal, + description = input.strCategory, + image = input.strMealThumb, + data = input.toJson(), + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMenuType.kt b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMenuType.kt new file mode 100644 index 0000000..fb3f249 --- /dev/null +++ b/app/src/main/java/com/qomunal/opensource/androidresearch/ui/main/MainMenuType.kt @@ -0,0 +1,15 @@ +package com.qomunal.opensource.androidresearch.ui.main + +/** + * Created by faisalamircs on 21/01/2024 + * ----------------------------------------- + * Name : Muhammad Faisal Amir + * E-mail : faisalamircs@gmail.com + * Github : github.com/amirisback + * ----------------------------------------- + */ + + +enum class MainMenuType { + NEWS, MEAL +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6bc1005..8b1cf53 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,13 +6,22 @@ android:layout_height="match_parent" tools:context=".ui.main.MainActivity"> - + + + app:menu="@menu/bottom_navigation_home_menu" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml new file mode 100644 index 0000000..c720768 --- /dev/null +++ b/app/src/main/res/layout/fragment_main.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_main.xml b/app/src/main/res/layout/item_main.xml new file mode 100644 index 0000000..b85d4e1 --- /dev/null +++ b/app/src/main/res/layout/item_main.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_navigation_home_menu.xml b/app/src/main/res/menu/bottom_navigation_home_menu.xml new file mode 100644 index 0000000..273d989 --- /dev/null +++ b/app/src/main/res/menu/bottom_navigation_home_menu.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file