diff --git a/app/src/main/java/org/openedx/app/MainFragment.kt b/app/src/main/java/org/openedx/app/MainFragment.kt index c2b5041c7..c58ec437f 100644 --- a/app/src/main/java/org/openedx/app/MainFragment.kt +++ b/app/src/main/java/org/openedx/app/MainFragment.kt @@ -3,6 +3,11 @@ package org.openedx.app import android.os.Bundle import android.view.Menu import android.view.View +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.core.os.bundleOf import androidx.core.view.forEach import androidx.fragment.app.Fragment @@ -14,9 +19,14 @@ import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.openedx.app.databinding.FragmentMainBinding import org.openedx.app.deeplink.HomeTab +import org.openedx.core.AppUpdateState +import org.openedx.core.AppUpdateState.wasUpgradeDialogClosed import org.openedx.core.adapter.NavigationFragmentAdapter +import org.openedx.core.presentation.dialog.appupgrade.AppUpgradeDialogFragment +import org.openedx.core.presentation.global.appupgrade.AppUpgradeRecommendedBox import org.openedx.core.presentation.global.appupgrade.UpgradeRequiredFragment import org.openedx.core.presentation.global.viewBinding +import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.discovery.presentation.DiscoveryRouter import org.openedx.downloads.presentation.download.DownloadsFragment import org.openedx.learn.presentation.LearnFragment @@ -45,6 +55,7 @@ class MainFragment : Fragment(R.layout.fragment_main) { handleArguments() setupBottomNavigation() setupViewPager() + setupBottomPopup() observeViewModel() } @@ -186,6 +197,56 @@ class MainFragment : Fragment(R.layout.fragment_main) { } } + private fun setupBottomPopup() { + binding.composeBottomPopup.setContent { + val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState() + val wasUpgradeDialogClosed by remember { wasUpgradeDialogClosed } + val appUpgradeParameters = AppUpdateState.AppUpgradeParameters( + appUpgradeEvent = appUpgradeEvent, + wasUpgradeDialogClosed = wasUpgradeDialogClosed, + appUpgradeRecommendedDialog = { + val dialog = AppUpgradeDialogFragment.newInstance() + dialog.show( + requireActivity().supportFragmentManager, + AppUpgradeDialogFragment::class.simpleName + ) + }, + onAppUpgradeRecommendedBoxClick = { + AppUpdateState.openPlayMarket(requireContext()) + }, + onAppUpgradeRequired = { + router.navigateToUpgradeRequired( + requireActivity().supportFragmentManager + ) + } + ) + when (appUpgradeParameters.appUpgradeEvent) { + is AppUpgradeEvent.UpgradeRecommendedEvent -> { + if (appUpgradeParameters.wasUpgradeDialogClosed) { + AppUpgradeRecommendedBox( + modifier = Modifier.fillMaxWidth(), + onClick = appUpgradeParameters.onAppUpgradeRecommendedBoxClick + ) + } else { + if (!AppUpdateState.wasUpdateDialogDisplayed) { + AppUpdateState.wasUpdateDialogDisplayed = true + appUpgradeParameters.appUpgradeRecommendedDialog() + } + } + } + + is AppUpgradeEvent.UpgradeRequiredEvent -> { + if (!AppUpdateState.wasUpdateDialogDisplayed) { + AppUpdateState.wasUpdateDialogDisplayed = true + appUpgradeParameters.onAppUpgradeRequired() + } + } + + else -> {} + } + } + } + companion object { private const val ARG_COURSE_ID = "courseId" private const val ARG_INFO_TYPE = "info_type" diff --git a/app/src/main/java/org/openedx/app/MainViewModel.kt b/app/src/main/java/org/openedx/app/MainViewModel.kt index 2d2033769..8723d6dbe 100644 --- a/app/src/main/java/org/openedx/app/MainViewModel.kt +++ b/app/src/main/java/org/openedx/app/MainViewModel.kt @@ -10,9 +10,12 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.openedx.core.config.Config import org.openedx.core.system.notifier.DiscoveryNotifier import org.openedx.core.system.notifier.NavigationToDiscovery +import org.openedx.core.system.notifier.app.AppNotifier +import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.discovery.presentation.DiscoveryNavigator import org.openedx.foundation.presentation.BaseViewModel @@ -20,6 +23,7 @@ class MainViewModel( private val config: Config, private val notifier: DiscoveryNotifier, private val analytics: AppAnalytics, + private val appNotifier: AppNotifier, ) : BaseViewModel() { private val _isBottomBarEnabled = MutableLiveData(true) @@ -30,6 +34,10 @@ class MainViewModel( val navigateToDiscovery: SharedFlow get() = _navigateToDiscovery.asSharedFlow() + private val _appUpgradeEvent = MutableLiveData() + val appUpgradeEvent: LiveData + get() = _appUpgradeEvent + val isDiscoveryTypeWebView get() = config.getDiscoveryConfig().isViewTypeWebView() val getDiscoveryFragment get() = DiscoveryNavigator(isDiscoveryTypeWebView).getDiscoveryFragment() @@ -37,14 +45,8 @@ class MainViewModel( override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) - notifier.notifier - .onEach { - if (it is NavigationToDiscovery) { - _navigateToDiscovery.emit(true) - } - } - .distinctUntilChanged() - .launchIn(viewModelScope) + collectDiscoveryEvents() + collectAppUpgradeEvent() } fun enableBottomBar(enable: Boolean) { @@ -75,4 +77,28 @@ class MainViewModel( } ) } + + private fun collectDiscoveryEvents() { + notifier.notifier + .onEach { + if (it is NavigationToDiscovery) { + _navigateToDiscovery.emit(true) + } + } + .distinctUntilChanged() + .launchIn(viewModelScope) + } + + private fun collectAppUpgradeEvent() { + viewModelScope.launch { + appNotifier.notifier + .onEach { event -> + if (event is AppUpgradeEvent) { + _appUpgradeEvent.value = event + } + } + .distinctUntilChanged() + .launchIn(viewModelScope) + } + } } diff --git a/app/src/main/java/org/openedx/app/data/networking/AppUpgradeInterceptor.kt b/app/src/main/java/org/openedx/app/data/networking/AppUpgradeInterceptor.kt index e789ed52b..e3add144d 100644 --- a/app/src/main/java/org/openedx/app/data/networking/AppUpgradeInterceptor.kt +++ b/app/src/main/java/org/openedx/app/data/networking/AppUpgradeInterceptor.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response import org.openedx.app.BuildConfig +import org.openedx.core.AppUpdateState import org.openedx.core.system.notifier.app.AppNotifier import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.core.utils.TimeUtils @@ -17,23 +18,30 @@ class AppUpgradeInterceptor( val responseCode = response.code val latestAppVersion = response.header(HEADER_APP_LATEST_VERSION) ?: "" val lastSupportedDateString = response.header(HEADER_APP_VERSION_LAST_SUPPORTED_DATE) ?: "" - val lastSupportedDateTime = TimeUtils.iso8601WithTimeZoneToDate(lastSupportedDateString)?.time ?: 0L + val lastSupportedDateTime = + TimeUtils.iso8601WithTimeZoneToDate(lastSupportedDateString)?.time ?: 0L runBlocking { - when { + val appUpgradeEvent = when { responseCode == 426 -> { - appNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent) + AppUpgradeEvent.UpgradeRequiredEvent } BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime > Date().time -> { - appNotifier.send(AppUpgradeEvent.UpgradeRecommendedEvent(latestAppVersion)) + AppUpgradeEvent.UpgradeRecommendedEvent(latestAppVersion) } latestAppVersion.isNotEmpty() && BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime < Date().time -> { - appNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent) + AppUpgradeEvent.UpgradeRequiredEvent + } + + else -> { + return@runBlocking } } + AppUpdateState.lastAppUpgradeEvent = appUpgradeEvent + appNotifier.send(appUpgradeEvent) } return response } diff --git a/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt b/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt index bdc7c6284..a4daf0809 100644 --- a/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt +++ b/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt @@ -1,14 +1,13 @@ package org.openedx.app.data.networking -import android.content.Context import okhttp3.Interceptor import okhttp3.Response -import org.openedx.app.BuildConfig import org.openedx.core.config.Config import org.openedx.core.data.storage.CorePreferences +import org.openedx.core.presentation.global.AppData class HeadersInterceptor( - private val context: Context, + private val appData: AppData, private val config: Config, private val preferencesManager: CorePreferences, ) : Interceptor { @@ -26,13 +25,7 @@ class HeadersInterceptor( addHeader("Accept", "application/json") val httpAgent = System.getProperty("http.agent") ?: "" - addHeader( - "User-Agent", - httpAgent + " " + - context.getString(org.openedx.core.R.string.app_name) + "/" + - BuildConfig.APPLICATION_ID + "/" + - BuildConfig.VERSION_NAME - ) + addHeader("User-Agent", "$httpAgent ${appData.versionName}") }.build() ) } diff --git a/app/src/main/java/org/openedx/app/di/ScreenModule.kt b/app/src/main/java/org/openedx/app/di/ScreenModule.kt index d00d0f1fe..464007259 100644 --- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt +++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt @@ -91,7 +91,7 @@ val screenModule = module { get(), ) } - viewModel { MainViewModel(get(), get(), get()) } + viewModel { MainViewModel(get(), get(), get(), get()) } factory { AuthRepository(get(), get(), get()) } factory { AuthInteractor(get()) } @@ -148,7 +148,7 @@ val screenModule = module { factory { DashboardRepository(get(), get(), get(), get()) } factory { DashboardInteractor(get()) } - viewModel { DashboardListViewModel(get(), get(), get(), get(), get(), get(), get()) } + viewModel { DashboardListViewModel(get(), get(), get(), get(), get(), get()) } viewModel { (windowSize: WindowSize) -> DashboardGalleryViewModel( get(), @@ -169,7 +169,7 @@ val screenModule = module { factory { DiscoveryRepository(get(), get(), get()) } factory { DiscoveryInteractor(get()) } - viewModel { NativeDiscoveryViewModel(get(), get(), get(), get(), get(), get(), get()) } + viewModel { NativeDiscoveryViewModel(get(), get(), get(), get(), get(), get()) } viewModel { (querySearch: String) -> WebViewDiscoveryViewModel( querySearch, diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 9a4861379..362793686 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -27,4 +27,11 @@ app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" /> + + diff --git a/core/src/main/java/org/openedx/core/AppUpdateState.kt b/core/src/main/java/org/openedx/core/AppUpdateState.kt index 0f92d145b..9c016581d 100644 --- a/core/src/main/java/org/openedx/core/AppUpdateState.kt +++ b/core/src/main/java/org/openedx/core/AppUpdateState.kt @@ -3,23 +3,29 @@ package org.openedx.core import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent -import android.net.Uri import androidx.compose.runtime.mutableStateOf +import androidx.core.net.toUri import org.openedx.core.system.notifier.app.AppUpgradeEvent object AppUpdateState { var wasUpdateDialogDisplayed = false - var wasUpdateDialogClosed = mutableStateOf(false) + var wasUpgradeDialogClosed = mutableStateOf(false) + var lastAppUpgradeEvent: AppUpgradeEvent? = null fun openPlayMarket(context: Context) { try { - context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=${context.packageName}"))) + context.startActivity( + Intent( + Intent.ACTION_VIEW, + "market://details?id=${context.packageName}".toUri() + ) + ) } catch (e: ActivityNotFoundException) { e.printStackTrace() context.startActivity( Intent( Intent.ACTION_VIEW, - Uri.parse("https://play.google.com/store/apps/details?id=${context.packageName}") + "https://play.google.com/store/apps/details?id=${context.packageName}".toUri() ) ) } @@ -27,7 +33,7 @@ object AppUpdateState { data class AppUpgradeParameters( val appUpgradeEvent: AppUpgradeEvent? = null, - val wasUpdateDialogClosed: Boolean = AppUpdateState.wasUpdateDialogClosed.value, + val wasUpgradeDialogClosed: Boolean = AppUpdateState.wasUpgradeDialogClosed.value, val appUpgradeRecommendedDialog: () -> Unit = {}, val onAppUpgradeRecommendedBoxClick: () -> Unit = {}, val onAppUpgradeRequired: () -> Unit = {}, diff --git a/core/src/main/java/org/openedx/core/presentation/dialog/appupgrade/AppUpgradeDialogFragment.kt b/core/src/main/java/org/openedx/core/presentation/dialog/appupgrade/AppUpgradeDialogFragment.kt index 6e7a4c301..a558e8b40 100644 --- a/core/src/main/java/org/openedx/core/presentation/dialog/appupgrade/AppUpgradeDialogFragment.kt +++ b/core/src/main/java/org/openedx/core/presentation/dialog/appupgrade/AppUpgradeDialogFragment.kt @@ -33,12 +33,12 @@ class AppUpgradeDialogFragment : DialogFragment() { } private fun onNotNowClick() { - AppUpdateState.wasUpdateDialogClosed.value = true + AppUpdateState.wasUpgradeDialogClosed.value = true dismiss() } private fun onUpdateClick() { - AppUpdateState.wasUpdateDialogClosed.value = true + AppUpdateState.wasUpgradeDialogClosed.value = true dismiss() AppUpdateState.openPlayMarket(requireContext()) } diff --git a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt index 642f6257a..55f995a01 100644 --- a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt +++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt @@ -73,7 +73,6 @@ import coil.compose.AsyncImage import coil.request.ImageRequest import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -import org.openedx.core.AppUpdateState import org.openedx.core.domain.model.Certificate import org.openedx.core.domain.model.CourseAssignments import org.openedx.core.domain.model.CourseSharingUtmParameters @@ -82,8 +81,6 @@ import org.openedx.core.domain.model.CoursewareAccess import org.openedx.core.domain.model.EnrolledCourse import org.openedx.core.domain.model.EnrolledCourseData import org.openedx.core.domain.model.Progress -import org.openedx.core.presentation.global.appupgrade.AppUpgradeRecommendedBox -import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.core.ui.HandleUIMessage import org.openedx.core.ui.OfflineModeDialog import org.openedx.core.ui.displayCutoutForLandscape @@ -127,7 +124,6 @@ class DashboardListFragment : Fragment() { val uiMessage by viewModel.uiMessage.observeAsState() val refreshing by viewModel.updating.observeAsState(false) val canLoadMore by viewModel.canLoadMore.observeAsState(false) - val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState() DashboardListView( windowSize = windowSize, @@ -154,12 +150,6 @@ class DashboardListFragment : Fragment() { paginationCallback = { viewModel.fetchMore() }, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters( - appUpgradeEvent = appUpgradeEvent, - onAppUpgradeRecommendedBoxClick = { - AppUpdateState.openPlayMarket(requireContext()) - }, - ), ) } } @@ -184,7 +174,6 @@ internal fun DashboardListView( onSwipeRefresh: () -> Unit, paginationCallback: () -> Unit, onItemClick: (EnrolledCourse) -> Unit, - appUpgradeParameters: AppUpdateState.AppUpgradeParameters, ) { val scaffoldState = rememberScaffoldState() val pullRefreshState = @@ -306,7 +295,11 @@ internal fun DashboardListView( } } ) - if (scrollState.shouldLoadMore(firstVisibleIndex, LOAD_MORE_THRESHOLD)) { + if (scrollState.shouldLoadMore( + firstVisibleIndex, + LOAD_MORE_THRESHOLD + ) + ) { paginationCallback() } } @@ -338,17 +331,6 @@ internal fun DashboardListView( .fillMaxWidth() .align(Alignment.BottomCenter) ) { - when (appUpgradeParameters.appUpgradeEvent) { - is AppUpgradeEvent.UpgradeRecommendedEvent -> { - AppUpgradeRecommendedBox( - modifier = Modifier.fillMaxWidth(), - onClick = appUpgradeParameters.onAppUpgradeRecommendedBoxClick - ) - } - - else -> {} - } - if (!isInternetConnectionShown && !hasInternetConnection) { OfflineModeDialog( Modifier @@ -564,7 +546,6 @@ private fun DashboardListViewPreview() { refreshing = false, canLoadMore = false, paginationCallback = {}, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters() ) } } @@ -595,7 +576,6 @@ private fun DashboardListViewTabletPreview() { refreshing = false, canLoadMore = false, paginationCallback = {}, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters() ) } } @@ -617,7 +597,6 @@ private fun EmptyStatePreview() { refreshing = false, canLoadMore = false, paginationCallback = {}, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters() ) } } diff --git a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListViewModel.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListViewModel.kt index e9945f18e..58f83b8f2 100644 --- a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListViewModel.kt +++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListViewModel.kt @@ -11,8 +11,6 @@ import org.openedx.core.domain.model.EnrolledCourse import org.openedx.core.system.connection.NetworkConnection import org.openedx.core.system.notifier.CourseDashboardUpdate import org.openedx.core.system.notifier.DiscoveryNotifier -import org.openedx.core.system.notifier.app.AppNotifier -import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.dashboard.domain.interactor.DashboardInteractor import org.openedx.foundation.extension.isInternetError import org.openedx.foundation.presentation.BaseViewModel @@ -27,7 +25,6 @@ class DashboardListViewModel( private val resourceManager: ResourceManager, private val discoveryNotifier: DiscoveryNotifier, private val analytics: DashboardAnalytics, - private val appNotifier: AppNotifier ) : BaseViewModel() { private val coursesList = mutableListOf() @@ -55,10 +52,6 @@ class DashboardListViewModel( val canLoadMore: LiveData get() = _canLoadMore - private val _appUpgradeEvent = MutableLiveData() - val appUpgradeEvent: LiveData - get() = _appUpgradeEvent - override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) viewModelScope.launch { @@ -72,7 +65,6 @@ class DashboardListViewModel( init { getCourses() - collectAppUpgradeEvent() } fun getCourses() { @@ -168,16 +160,6 @@ class DashboardListViewModel( } } - private fun collectAppUpgradeEvent() { - viewModelScope.launch { - appNotifier.notifier.collect { event -> - if (event is AppUpgradeEvent) { - _appUpgradeEvent.value = event - } - } - } - } - fun dashboardCourseClickedEvent(courseId: String, courseName: String) { analytics.dashboardCourseClickedEvent(courseId, courseName) } diff --git a/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardListViewModelTest.kt b/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardListViewModelTest.kt index 4a54b8f36..fae8a9455 100644 --- a/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardListViewModelTest.kt +++ b/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardListViewModelTest.kt @@ -8,10 +8,8 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import io.mockk.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle @@ -31,7 +29,6 @@ import org.openedx.core.domain.model.Pagination import org.openedx.core.system.connection.NetworkConnection import org.openedx.core.system.notifier.CourseDashboardUpdate import org.openedx.core.system.notifier.DiscoveryNotifier -import org.openedx.core.system.notifier.app.AppNotifier import org.openedx.dashboard.domain.interactor.DashboardInteractor import org.openedx.foundation.presentation.UIMessage import org.openedx.foundation.system.ResourceManager @@ -51,7 +48,6 @@ class DashboardListViewModelTest { private val networkConnection = mockk() private val discoveryNotifier = mockk() private val analytics = mockk() - private val appNotifier = mockk() private val noInternet = "Slow or no internet connection" private val somethingWrong = "Something went wrong" @@ -66,7 +62,6 @@ class DashboardListViewModelTest { Dispatchers.setMain(dispatcher) every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong - every { appNotifier.notifier } returns emptyFlow() every { config.getApiHostURL() } returns "http://localhost:8000" } @@ -84,7 +79,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) every { networkConnection.isOnline() } returns true coEvery { interactor.getEnrolledCourses(any()) } throws UnknownHostException() @@ -92,7 +86,6 @@ class DashboardListViewModelTest { coVerify(exactly = 1) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage assertEquals(noInternet, message?.message) @@ -108,7 +101,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) every { networkConnection.isOnline() } returns true coEvery { interactor.getEnrolledCourses(any()) } throws Exception() @@ -116,7 +108,6 @@ class DashboardListViewModelTest { coVerify(exactly = 1) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage assertEquals(somethingWrong, message?.message) @@ -132,7 +123,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) every { networkConnection.isOnline() } returns true coEvery { interactor.getEnrolledCourses(any()) } returns dashboardCourseList @@ -141,7 +131,6 @@ class DashboardListViewModelTest { coVerify(exactly = 1) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } assert(viewModel.uiMessage.value == null) assert(viewModel.uiState.value is DashboardUIState.Courses) @@ -156,7 +145,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) every { networkConnection.isOnline() } returns true coEvery { interactor.getEnrolledCourses(any()) } returns dashboardCourseList.copy( @@ -173,7 +161,6 @@ class DashboardListViewModelTest { coVerify(exactly = 1) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } assert(viewModel.uiMessage.value == null) assert(viewModel.uiState.value is DashboardUIState.Courses) @@ -190,14 +177,12 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) advanceUntilIdle() coVerify(exactly = 0) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 1) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } assert(viewModel.uiMessage.value == null) assert(viewModel.uiState.value is DashboardUIState.Courses) @@ -214,7 +199,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) coEvery { interactor.getEnrolledCourses(any()) } throws UnknownHostException() @@ -223,7 +207,6 @@ class DashboardListViewModelTest { coVerify(exactly = 2) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage assertEquals(noInternet, message?.message) @@ -242,7 +225,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) coEvery { interactor.getEnrolledCourses(any()) } throws Exception() @@ -251,7 +233,6 @@ class DashboardListViewModelTest { coVerify(exactly = 2) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage assertEquals(somethingWrong, message?.message) @@ -270,7 +251,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) viewModel.updateCourses() @@ -278,8 +258,6 @@ class DashboardListViewModelTest { coVerify(exactly = 2) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } - assert(viewModel.uiMessage.value == null) assert(viewModel.updating.value == false) assert(viewModel.uiState.value is DashboardUIState.Courses) @@ -303,7 +281,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) viewModel.updateCourses() @@ -311,8 +288,6 @@ class DashboardListViewModelTest { coVerify(exactly = 2) { interactor.getEnrolledCourses(any()) } coVerify(exactly = 0) { interactor.getEnrolledCoursesFromCache() } - verify(exactly = 1) { appNotifier.notifier } - assert(viewModel.uiMessage.value == null) assert(viewModel.updating.value == false) assert(viewModel.uiState.value is DashboardUIState.Courses) @@ -328,7 +303,6 @@ class DashboardListViewModelTest { resourceManager, discoveryNotifier, analytics, - appNotifier ) val mockLifeCycleOwner: LifecycleOwner = mockk() @@ -339,6 +313,5 @@ class DashboardListViewModelTest { advanceUntilIdle() coVerify(exactly = 1) { interactor.getEnrolledCourses(any()) } - verify(exactly = 1) { appNotifier.notifier } } } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt index 28976b4a7..2212849b5 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt @@ -57,12 +57,7 @@ import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -import org.openedx.core.AppUpdateState -import org.openedx.core.AppUpdateState.wasUpdateDialogClosed import org.openedx.core.domain.model.Media -import org.openedx.core.presentation.dialog.appupgrade.AppUpgradeDialogFragment -import org.openedx.core.presentation.global.appupgrade.AppUpgradeRecommendedBox -import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.core.ui.AuthButtonsPanel import org.openedx.core.ui.BackBtn import org.openedx.core.ui.HandleUIMessage @@ -104,8 +99,6 @@ class NativeDiscoveryFragment : Fragment() { val uiMessage by viewModel.uiMessage.observeAsState() val canLoadMore by viewModel.canLoadMore.observeAsState(false) val refreshing by viewModel.isUpdating.observeAsState(false) - val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState() - val wasUpdateDialogClosed by remember { wasUpdateDialogClosed } val querySearch = arguments?.getString(ARG_SEARCH_QUERY, "") ?: "" DiscoveryScreen( @@ -119,25 +112,6 @@ class NativeDiscoveryFragment : Fragment() { canShowBackButton = viewModel.canShowBackButton, isUserLoggedIn = viewModel.isUserLoggedIn, isRegistrationEnabled = viewModel.isRegistrationEnabled, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters( - appUpgradeEvent = appUpgradeEvent, - wasUpdateDialogClosed = wasUpdateDialogClosed, - appUpgradeRecommendedDialog = { - val dialog = AppUpgradeDialogFragment.newInstance() - dialog.show( - requireActivity().supportFragmentManager, - AppUpgradeDialogFragment::class.simpleName - ) - }, - onAppUpgradeRecommendedBoxClick = { - AppUpdateState.openPlayMarket(requireContext()) - }, - onAppUpgradeRequired = { - router.navigateToUpgradeRequired( - requireActivity().supportFragmentManager - ) - } - ), onSearchClick = { viewModel.discoverySearchBarClickedEvent() router.navigateToCourseSearch( @@ -214,7 +188,6 @@ internal fun DiscoveryScreen( canShowBackButton: Boolean, isUserLoggedIn: Boolean, isRegistrationEnabled: Boolean, - appUpgradeParameters: AppUpdateState.AppUpgradeParameters, onSearchClick: () -> Unit, onSwipeRefresh: () -> Unit, onReloadClick: () -> Unit, @@ -419,7 +392,11 @@ internal fun DiscoveryScreen( } } } - if (scrollState.shouldLoadMore(firstVisibleIndex, LOAD_MORE_THRESHOLD)) { + if (scrollState.shouldLoadMore( + firstVisibleIndex, + LOAD_MORE_THRESHOLD + ) + ) { paginationCallback() } } @@ -436,30 +413,6 @@ internal fun DiscoveryScreen( .fillMaxWidth() .align(Alignment.BottomCenter) ) { - when (appUpgradeParameters.appUpgradeEvent) { - is AppUpgradeEvent.UpgradeRecommendedEvent -> { - if (appUpgradeParameters.wasUpdateDialogClosed) { - AppUpgradeRecommendedBox( - modifier = Modifier.fillMaxWidth(), - onClick = appUpgradeParameters.onAppUpgradeRecommendedBoxClick - ) - } else { - if (!AppUpdateState.wasUpdateDialogDisplayed) { - AppUpdateState.wasUpdateDialogDisplayed = true - appUpgradeParameters.appUpgradeRecommendedDialog() - } - } - } - - is AppUpgradeEvent.UpgradeRequiredEvent -> { - if (!AppUpdateState.wasUpdateDialogDisplayed) { - AppUpdateState.wasUpdateDialogDisplayed = true - appUpgradeParameters.onAppUpgradeRequired() - } - } - - else -> {} - } if (!isInternetConnectionShown && !hasInternetConnection) { OfflineModeDialog( Modifier @@ -526,7 +479,6 @@ private fun DiscoveryScreenPreview() { hasInternetConnection = true, isUserLoggedIn = false, isRegistrationEnabled = true, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters(), onSignInClick = {}, onRegisterClick = {}, onBackClick = {}, @@ -568,7 +520,6 @@ private fun DiscoveryScreenTabletPreview() { hasInternetConnection = true, isUserLoggedIn = true, isRegistrationEnabled = true, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters(), onSignInClick = {}, onRegisterClick = {}, onBackClick = {}, diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryViewModel.kt b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryViewModel.kt index 0d4673e23..70acffbd8 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryViewModel.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryViewModel.kt @@ -3,15 +3,11 @@ package org.openedx.discovery.presentation import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch import org.openedx.core.R import org.openedx.core.config.Config import org.openedx.core.data.storage.CorePreferences import org.openedx.core.system.connection.NetworkConnection -import org.openedx.core.system.notifier.app.AppNotifier -import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.discovery.domain.interactor.DiscoveryInteractor import org.openedx.discovery.domain.model.Course import org.openedx.foundation.extension.isInternetError @@ -26,7 +22,6 @@ class NativeDiscoveryViewModel( private val interactor: DiscoveryInteractor, private val resourceManager: ResourceManager, private val analytics: DiscoveryAnalytics, - private val appNotifier: AppNotifier, private val corePreferences: CorePreferences, ) : BaseViewModel() { @@ -51,10 +46,6 @@ class NativeDiscoveryViewModel( val isUpdating: LiveData get() = _isUpdating - private val _appUpgradeEvent = MutableLiveData() - val appUpgradeEvent: LiveData - get() = _appUpgradeEvent - val hasInternetConnection: Boolean get() = networkConnection.isOnline() @@ -64,7 +55,6 @@ class NativeDiscoveryViewModel( init { getCoursesList() - collectAppUpgradeEvent() } private fun loadCoursesInternal( @@ -159,24 +149,6 @@ class NativeDiscoveryViewModel( } } - @OptIn(FlowPreview::class) - private fun collectAppUpgradeEvent() { - viewModelScope.launch { - appNotifier.notifier - .debounce(100) - .collect { event -> - when (event) { - is AppUpgradeEvent.UpgradeRecommendedEvent -> { - _appUpgradeEvent.value = event - } - is AppUpgradeEvent.UpgradeRequiredEvent -> { - _appUpgradeEvent.value = AppUpgradeEvent.UpgradeRequiredEvent - } - } - } - } - } - fun discoverySearchBarClickedEvent() { analytics.discoverySearchBarClickedEvent() } diff --git a/discovery/src/test/java/org/openedx/discovery/presentation/NativeDiscoveryViewModelTest.kt b/discovery/src/test/java/org/openedx/discovery/presentation/NativeDiscoveryViewModelTest.kt index 9a88b445a..d6270fe7b 100644 --- a/discovery/src/test/java/org/openedx/discovery/presentation/NativeDiscoveryViewModelTest.kt +++ b/discovery/src/test/java/org/openedx/discovery/presentation/NativeDiscoveryViewModelTest.kt @@ -5,10 +5,8 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import io.mockk.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain @@ -25,7 +23,6 @@ import org.openedx.core.config.Config import org.openedx.core.data.storage.CorePreferences import org.openedx.core.domain.model.Pagination import org.openedx.core.system.connection.NetworkConnection -import org.openedx.core.system.notifier.app.AppNotifier import org.openedx.discovery.domain.interactor.DiscoveryInteractor import org.openedx.discovery.domain.model.CourseList import org.openedx.foundation.presentation.UIMessage @@ -45,7 +42,6 @@ class NativeDiscoveryViewModelTest { private val interactor = mockk() private val networkConnection = mockk() private val analytics = mockk() - private val appNotifier = mockk() private val corePreferences = mockk() private val noInternet = "Slow or no internet connection" @@ -56,7 +52,6 @@ class NativeDiscoveryViewModelTest { Dispatchers.setMain(dispatcher) every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong - every { appNotifier.notifier } returns emptyFlow() every { corePreferences.user } returns null every { config.getApiHostURL() } returns "http://localhost:8000" every { config.isPreLoginExperienceEnabled() } returns false @@ -75,7 +70,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -84,7 +78,6 @@ class NativeDiscoveryViewModelTest { coVerify(exactly = 1) { interactor.getCoursesList(any(), any(), any()) } coVerify(exactly = 0) { interactor.getCoursesListFromCache() } - verify(exactly = 1) { appNotifier.notifier } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage assertEquals(noInternet, message?.message) @@ -100,7 +93,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -124,7 +116,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns false @@ -147,7 +138,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -178,7 +168,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -209,7 +198,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -234,7 +222,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -259,7 +246,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true @@ -291,7 +277,6 @@ class NativeDiscoveryViewModelTest { interactor, resourceManager, analytics, - appNotifier, corePreferences ) every { networkConnection.isOnline() } returns true diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt index 217a35258..f1eaf0aeb 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt @@ -28,12 +28,10 @@ class SettingsFragment : Fragment() { val windowSize = rememberWindowSize() val uiState by viewModel.uiState.collectAsState() val logoutSuccess by viewModel.successLogout.collectAsState(false) - val appUpgradeEvent by viewModel.appUpgradeEvent.collectAsState(null) SettingsScreen( windowSize = windowSize, uiState = uiState, - appUpgradeEvent = appUpgradeEvent, onBackClick = { requireActivity().supportFragmentManager.popBackStack() }, diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt index 68c773745..6122775bf 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import org.openedx.core.AppUpdateState import org.openedx.core.R import org.openedx.core.domain.model.AgreementUrls import org.openedx.core.presentation.global.AppData @@ -75,7 +76,6 @@ import org.openedx.profile.R as profileR internal fun SettingsScreen( windowSize: WindowSize, uiState: SettingsUIState, - appUpgradeEvent: AppUpgradeEvent?, onBackClick: () -> Unit, onAction: (SettingsScreenAction) -> Unit, ) { @@ -189,7 +189,6 @@ internal fun SettingsScreen( SupportInfoSection( uiState = uiState, onAction = onAction, - appUpgradeEvent = appUpgradeEvent, ) Spacer(modifier = Modifier.height(24.dp)) @@ -264,7 +263,6 @@ private fun ManageAccountSection(onManageAccountClick: () -> Unit) { @Composable private fun SupportInfoSection( uiState: SettingsUIState.Data, - appUpgradeEvent: AppUpgradeEvent?, onAction: (SettingsScreenAction) -> Unit ) { Column { @@ -325,7 +323,7 @@ private fun SupportInfoSection( } AppVersionItem( versionName = uiState.configuration.versionName, - appUpgradeEvent = appUpgradeEvent, + appUpgradeEvent = AppUpdateState.lastAppUpgradeEvent, ) { onAction(SettingsScreenAction.AppVersionClick) } @@ -692,7 +690,6 @@ private fun SettingsScreenPreview() { windowSize = WindowSize(WindowType.Medium, WindowType.Medium), uiState = mockUiState, onAction = {}, - appUpgradeEvent = null, ) } } diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt index 59548d1c9..c21f72df3 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt @@ -21,7 +21,6 @@ import org.openedx.core.module.DownloadWorkerController import org.openedx.core.presentation.global.AppData import org.openedx.core.system.AppCookieManager import org.openedx.core.system.notifier.app.AppNotifier -import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.core.system.notifier.app.LogoutEvent import org.openedx.core.utils.EmailUtil import org.openedx.foundation.extension.isInternetError @@ -62,10 +61,6 @@ class SettingsViewModel( val uiMessage: SharedFlow get() = _uiMessage.asSharedFlow() - private val _appUpgradeEvent = MutableStateFlow(null) - val appUpgradeEvent: StateFlow - get() = _appUpgradeEvent.asStateFlow() - val isLogistrationEnabled get() = config.isPreLoginExperienceEnabled() private val configuration @@ -77,7 +72,6 @@ class SettingsViewModel( ) init { - collectAppUpgradeEvent() collectProfileEvent() } @@ -117,16 +111,6 @@ class SettingsViewModel( } } - private fun collectAppUpgradeEvent() { - viewModelScope.launch { - appNotifier.notifier.collect { event -> - if (event is AppUpgradeEvent) { - _appUpgradeEvent.value = event - } - } - } - } - private fun collectProfileEvent() { viewModelScope.launch { profileNotifier.notifier.collect {