11package org.openedx.app
22
33import android.os.Bundle
4+ import android.view.Menu
45import android.view.View
6+ import androidx.compose.foundation.layout.fillMaxWidth
7+ import androidx.compose.runtime.getValue
8+ import androidx.compose.runtime.livedata.observeAsState
9+ import androidx.compose.runtime.remember
10+ import androidx.compose.ui.Modifier
511import androidx.core.os.bundleOf
612import androidx.core.view.forEach
713import androidx.fragment.app.Fragment
@@ -13,10 +19,16 @@ import org.koin.android.ext.android.inject
1319import org.koin.androidx.viewmodel.ext.android.viewModel
1420import org.openedx.app.databinding.FragmentMainBinding
1521import org.openedx.app.deeplink.HomeTab
22+ import org.openedx.core.AppUpdateState
23+ import org.openedx.core.AppUpdateState.wasUpgradeDialogClosed
1624import org.openedx.core.adapter.NavigationFragmentAdapter
25+ import org.openedx.core.presentation.dialog.appupgrade.AppUpgradeDialogFragment
26+ import org.openedx.core.presentation.global.appupgrade.AppUpgradeRecommendedBox
1727import org.openedx.core.presentation.global.appupgrade.UpgradeRequiredFragment
1828import org.openedx.core.presentation.global.viewBinding
29+ import org.openedx.core.system.notifier.app.AppUpgradeEvent
1930import org.openedx.discovery.presentation.DiscoveryRouter
31+ import org.openedx.downloads.presentation.download.DownloadsFragment
2032import org.openedx.learn.presentation.LearnFragment
2133import org.openedx.learn.presentation.LearnTab
2234import org.openedx.profile.presentation.profile.ProfileFragment
@@ -40,29 +52,105 @@ class MainFragment : Fragment(R.layout.fragment_main) {
4052
4153 override fun onViewCreated (view : View , savedInstanceState : Bundle ? ) {
4254 super .onViewCreated(view, savedInstanceState)
55+ handleArguments()
56+ setupBottomNavigation()
57+ setupViewPager()
58+ setupBottomPopup()
59+ observeViewModel()
60+ }
4361
44- initViewPager()
45-
46- binding.bottomNavView.setOnItemSelectedListener {
47- when (it.itemId) {
48- R .id.fragmentLearn -> {
49- viewModel.logLearnTabClickedEvent()
50- binding.viewPager.setCurrentItem(0 , false )
62+ private fun handleArguments () {
63+ requireArguments().apply {
64+ getString(ARG_COURSE_ID ).takeIf { it.isNullOrBlank().not () }?.let { courseId ->
65+ val infoType = getString(ARG_INFO_TYPE )
66+ if (viewModel.isDiscoveryTypeWebView && infoType != null ) {
67+ router.navigateToCourseInfo(parentFragmentManager, courseId, infoType)
68+ } else {
69+ router.navigateToCourseDetail(parentFragmentManager, courseId)
5170 }
71+ putString(ARG_COURSE_ID , " " )
72+ putString(ARG_INFO_TYPE , " " )
73+ }
74+ }
75+ }
5276
53- R .id.fragmentDiscover -> {
54- viewModel.logDiscoveryTabClickedEvent( )
55- binding.viewPager.setCurrentItem( 1 , false )
56- }
77+ private fun setupBottomNavigation () {
78+ val openTabArg = requireArguments().getString( ARG_OPEN_TAB , HomeTab . LEARN .name )
79+ val initialMenuId = getInitialMenuId(openTabArg )
80+ binding.bottomNavView.selectedItemId = initialMenuId
5781
58- R .id.fragmentProfile -> {
59- viewModel.logProfileTabClickedEvent()
60- binding.viewPager.setCurrentItem(2 , false )
61- }
82+ val menu = binding.bottomNavView.menu
83+ menu.clear()
84+
85+ val tabList = createTabList(openTabArg)
86+ addMenuItems(menu, tabList)
87+ setupBottomNavListener(tabList)
88+
89+ requireArguments().remove(ARG_OPEN_TAB )
90+ }
91+
92+ private fun createTabList (openTabArg : String ): List <Pair <Int , Fragment >> {
93+ val learnFragment = LearnFragment .newInstance(
94+ openTab = if (openTabArg == HomeTab .PROGRAMS .name) {
95+ LearnTab .PROGRAMS .name
96+ } else {
97+ LearnTab .COURSES .name
98+ }
99+ )
100+
101+ return mutableListOf<Pair <Int , Fragment >>().apply {
102+ add(R .id.fragmentLearn to learnFragment)
103+ add(R .id.fragmentDiscover to viewModel.getDiscoveryFragment)
104+ if (viewModel.isDownloadsFragmentEnabled) {
105+ add(R .id.fragmentDownloads to DownloadsFragment ())
106+ }
107+ add(R .id.fragmentProfile to ProfileFragment ())
108+ }
109+ }
110+
111+ private fun addMenuItems (menu : Menu , tabList : List <Pair <Int , Fragment >>) {
112+ val tabTitles = mapOf (
113+ R .id.fragmentLearn to resources.getString(R .string.app_navigation_learn),
114+ R .id.fragmentDiscover to resources.getString(R .string.app_navigation_discovery),
115+ R .id.fragmentDownloads to resources.getString(R .string.app_navigation_downloads),
116+ R .id.fragmentProfile to resources.getString(R .string.app_navigation_profile),
117+ )
118+ val tabIconSelectors = mapOf (
119+ R .id.fragmentLearn to R .drawable.app_ic_learn_selector,
120+ R .id.fragmentDiscover to R .drawable.app_ic_discover_selector,
121+ R .id.fragmentDownloads to R .drawable.app_ic_downloads_selector,
122+ R .id.fragmentProfile to R .drawable.app_ic_profile_selector
123+ )
124+
125+ for ((id, _) in tabList) {
126+ val menuItem = menu.add(Menu .NONE , id, Menu .NONE , tabTitles[id] ? : " " )
127+ tabIconSelectors[id]?.let { menuItem.setIcon(it) }
128+ }
129+ }
130+
131+ private fun setupBottomNavListener (tabList : List <Pair <Int , Fragment >>) {
132+ val menuIdToIndex = tabList.mapIndexed { index, pair -> pair.first to index }.toMap()
133+
134+ binding.bottomNavView.setOnItemSelectedListener { menuItem ->
135+ when (menuItem.itemId) {
136+ R .id.fragmentLearn -> viewModel.logLearnTabClickedEvent()
137+ R .id.fragmentDiscover -> viewModel.logDiscoveryTabClickedEvent()
138+ R .id.fragmentDownloads -> viewModel.logDownloadsTabClickedEvent()
139+ R .id.fragmentProfile -> viewModel.logProfileTabClickedEvent()
140+ }
141+ menuIdToIndex[menuItem.itemId]?.let { index ->
142+ binding.viewPager.setCurrentItem(index, false )
62143 }
63144 true
64145 }
146+ }
147+
148+ private fun setupViewPager () {
149+ val tabList = createTabList(requireArguments().getString(ARG_OPEN_TAB , HomeTab .LEARN .name))
150+ initViewPager(tabList)
151+ }
65152
153+ private fun observeViewModel () {
66154 viewModel.isBottomBarEnabled.observe(viewLifecycleOwner) { isBottomBarEnabled ->
67155 enableBottomBar(isBottomBarEnabled)
68156 }
@@ -74,55 +162,30 @@ class MainFragment : Fragment(R.layout.fragment_main) {
74162 }
75163 }
76164 }
165+ }
77166
78- requireArguments().apply {
79- getString(ARG_COURSE_ID ).takeIf { it.isNullOrBlank().not () }?.let { courseId ->
80- val infoType = getString(ARG_INFO_TYPE )
81-
82- if (viewModel.isDiscoveryTypeWebView && infoType != null ) {
83- router.navigateToCourseInfo(parentFragmentManager, courseId, infoType)
84- } else {
85- router.navigateToCourseDetail(parentFragmentManager, courseId)
86- }
87-
88- // Clear arguments after navigation
89- putString(ARG_COURSE_ID , " " )
90- putString(ARG_INFO_TYPE , " " )
91- }
92-
93- when (requireArguments().getString(ARG_OPEN_TAB , " " )) {
94- HomeTab .LEARN .name,
95- HomeTab .PROGRAMS .name -> {
96- binding.bottomNavView.selectedItemId = R .id.fragmentLearn
97- }
98-
99- HomeTab .DISCOVER .name -> {
100- binding.bottomNavView.selectedItemId = R .id.fragmentDiscover
101- }
102-
103- HomeTab .PROFILE .name -> {
104- binding.bottomNavView.selectedItemId = R .id.fragmentProfile
105- }
167+ private fun getInitialMenuId (openTabArg : String ): Int {
168+ return when (openTabArg) {
169+ HomeTab .LEARN .name, HomeTab .PROGRAMS .name -> R .id.fragmentLearn
170+ HomeTab .DISCOVER .name -> R .id.fragmentDiscover
171+ HomeTab .DOWNLOADS .name -> if (viewModel.isDownloadsFragmentEnabled) {
172+ R .id.fragmentDownloads
173+ } else {
174+ R .id.fragmentLearn
106175 }
107- requireArguments().remove(ARG_OPEN_TAB )
176+ HomeTab .PROFILE .name -> R .id.fragmentProfile
177+ else -> R .id.fragmentLearn
108178 }
109179 }
110180
111- @Suppress(" MagicNumber" )
112- private fun initViewPager () {
181+ private fun initViewPager (tabList : List <Pair <Int , Fragment >>) {
113182 binding.viewPager.orientation = ViewPager2 .ORIENTATION_HORIZONTAL
114- binding.viewPager.offscreenPageLimit = 4
183+ binding.viewPager.offscreenPageLimit = tabList.size
115184
116- val openTab = requireArguments().getString(ARG_OPEN_TAB , HomeTab .LEARN .name)
117- val learnTab = if (openTab == HomeTab .PROGRAMS .name) {
118- LearnTab .PROGRAMS
119- } else {
120- LearnTab .COURSES
121- }
122185 adapter = NavigationFragmentAdapter (this ).apply {
123- addFragment( LearnFragment .newInstance(openTab = learnTab.name))
124- addFragment(viewModel.getDiscoveryFragment )
125- addFragment( ProfileFragment ())
186+ tabList.forEach { (_, fragment) ->
187+ addFragment(fragment )
188+ }
126189 }
127190 binding.viewPager.adapter = adapter
128191 binding.viewPager.isUserInputEnabled = false
@@ -134,6 +197,56 @@ class MainFragment : Fragment(R.layout.fragment_main) {
134197 }
135198 }
136199
200+ private fun setupBottomPopup () {
201+ binding.composeBottomPopup.setContent {
202+ val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState()
203+ val wasUpgradeDialogClosed by remember { wasUpgradeDialogClosed }
204+ val appUpgradeParameters = AppUpdateState .AppUpgradeParameters (
205+ appUpgradeEvent = appUpgradeEvent,
206+ wasUpgradeDialogClosed = wasUpgradeDialogClosed,
207+ appUpgradeRecommendedDialog = {
208+ val dialog = AppUpgradeDialogFragment .newInstance()
209+ dialog.show(
210+ requireActivity().supportFragmentManager,
211+ AppUpgradeDialogFragment ::class .simpleName
212+ )
213+ },
214+ onAppUpgradeRecommendedBoxClick = {
215+ AppUpdateState .openPlayMarket(requireContext())
216+ },
217+ onAppUpgradeRequired = {
218+ router.navigateToUpgradeRequired(
219+ requireActivity().supportFragmentManager
220+ )
221+ }
222+ )
223+ when (appUpgradeParameters.appUpgradeEvent) {
224+ is AppUpgradeEvent .UpgradeRecommendedEvent -> {
225+ if (appUpgradeParameters.wasUpgradeDialogClosed) {
226+ AppUpgradeRecommendedBox (
227+ modifier = Modifier .fillMaxWidth(),
228+ onClick = appUpgradeParameters.onAppUpgradeRecommendedBoxClick
229+ )
230+ } else {
231+ if (! AppUpdateState .wasUpdateDialogDisplayed) {
232+ AppUpdateState .wasUpdateDialogDisplayed = true
233+ appUpgradeParameters.appUpgradeRecommendedDialog()
234+ }
235+ }
236+ }
237+
238+ is AppUpgradeEvent .UpgradeRequiredEvent -> {
239+ if (! AppUpdateState .wasUpdateDialogDisplayed) {
240+ AppUpdateState .wasUpdateDialogDisplayed = true
241+ appUpgradeParameters.onAppUpgradeRequired()
242+ }
243+ }
244+
245+ else -> {}
246+ }
247+ }
248+ }
249+
137250 companion object {
138251 private const val ARG_COURSE_ID = " courseId"
139252 private const val ARG_INFO_TYPE = " info_type"
0 commit comments