Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/app update #79

Merged
merged 14 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ android {
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
versionName "1.0.0"

resourceConfigurations += ["en", "uk"]

Expand Down
17 changes: 16 additions & 1 deletion app/src/main/java/org/openedx/app/AppRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import org.openedx.auth.presentation.signup.SignUpFragment
import org.openedx.core.FragmentViewType
import org.openedx.core.domain.model.CoursewareAccess
import org.openedx.core.presentation.course.CourseViewMode
import org.openedx.core.presentation.global.app_upgrade.AppUpgradeRouter
import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment
import org.openedx.course.presentation.CourseRouter
import org.openedx.course.presentation.container.CourseContainerFragment
import org.openedx.course.presentation.container.NoAccessCourseContainerFragment
Expand All @@ -36,14 +38,15 @@ import org.openedx.profile.presentation.ProfileRouter
import org.openedx.profile.presentation.anothers_account.AnothersProfileFragment
import org.openedx.profile.presentation.delete.DeleteProfileFragment
import org.openedx.profile.presentation.edit.EditProfileFragment
import org.openedx.profile.presentation.profile.ProfileFragment
import org.openedx.profile.presentation.settings.video.VideoQualityFragment
import org.openedx.profile.presentation.settings.video.VideoSettingsFragment
import org.openedx.whatsnew.WhatsNewRouter
import org.openedx.whatsnew.presentation.whatsnew.WhatsNewFragment
import java.util.Date

class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, DiscussionRouter,
ProfileRouter, WhatsNewRouter {
ProfileRouter, AppUpgradeRouter, WhatsNewRouter {

//region AuthRouter
override fun navigateToMain(fm: FragmentManager) {
Expand Down Expand Up @@ -77,6 +80,10 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
override fun navigateToCourseSearch(fm: FragmentManager) {
replaceFragmentWithBackStack(fm, CourseSearchFragment())
}

override fun navigateToUpgradeRequired(fm: FragmentManager) {
replaceFragmentWithBackStack(fm, UpgradeRequiredFragment())
}
//endregion

//region DashboardRouter
Expand Down Expand Up @@ -289,4 +296,12 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
.replace(R.id.container, fragment, fragment.javaClass.simpleName)
.commit()
}

//App upgrade
override fun navigateToUserProfile(fm: FragmentManager) {
fm.popBackStack()
fm.beginTransaction()
.replace(R.id.container, ProfileFragment())
.commit()
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/org/openedx/app/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class AppViewModel(
private val analytics: AppAnalytics
) : BaseViewModel() {

private val _logoutUser = SingleEventLiveData<Unit>()
val logoutUser: LiveData<Unit>
get() = _logoutUser
private val _logoutUser = SingleEventLiveData<Unit>()

private var logoutHandledAt: Long = 0

Expand Down
33 changes: 29 additions & 4 deletions app/src/main/java/org/openedx/app/MainFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,35 @@ package org.openedx.app

import android.os.Bundle
import android.view.View
import androidx.viewpager2.widget.ViewPager2
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResultListener
import androidx.viewpager2.widget.ViewPager2
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.openedx.app.adapter.MainNavigationFragmentAdapter
import org.openedx.app.databinding.FragmentMainBinding
import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment
import org.openedx.core.presentation.global.viewBinding
import org.openedx.dashboard.presentation.DashboardFragment
import org.openedx.discovery.presentation.DiscoveryFragment
import org.openedx.app.adapter.MainNavigationFragmentAdapter
import org.openedx.profile.presentation.profile.ProfileFragment
import org.koin.android.ext.android.inject
import org.openedx.app.databinding.FragmentMainBinding

class MainFragment : Fragment(R.layout.fragment_main) {

private val binding by viewBinding(FragmentMainBinding::bind)
private val analytics by inject<AppAnalytics>()
private val viewModel by viewModel<MainViewModel>()

private lateinit var adapter: MainNavigationFragmentAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener(UpgradeRequiredFragment.REQUEST_KEY) { _, _ ->
binding.bottomNavView.selectedItemId = R.id.fragmentProfile
viewModel.enableBottomBar(false)
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

Expand All @@ -30,21 +42,28 @@ class MainFragment : Fragment(R.layout.fragment_main) {
analytics.discoveryTabClickedEvent()
binding.viewPager.setCurrentItem(0, false)
}

R.id.fragmentDashboard -> {
analytics.dashboardTabClickedEvent()
binding.viewPager.setCurrentItem(1, false)
}

R.id.fragmentPrograms -> {
analytics.programsTabClickedEvent()
binding.viewPager.setCurrentItem(2, false)
}

R.id.fragmentProfile -> {
analytics.profileTabClickedEvent()
binding.viewPager.setCurrentItem(3, false)
}
}
true
}

viewModel.isBottomBarEnabled.observe(viewLifecycleOwner) { isBottomBarEnabled ->
enableBottomBar(isBottomBarEnabled)
}
}

private fun initViewPager() {
Expand All @@ -59,4 +78,10 @@ class MainFragment : Fragment(R.layout.fragment_main) {
binding.viewPager.adapter = adapter
binding.viewPager.isUserInputEnabled = false
}

private fun enableBottomBar(enable: Boolean) {
for (i in 0 until binding.bottomNavView.menu.size()) {
binding.bottomNavView.menu.getItem(i).isEnabled = enable
}
}
}
15 changes: 15 additions & 0 deletions app/src/main/java/org/openedx/app/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.openedx.app

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import org.openedx.core.BaseViewModel

class MainViewModel: BaseViewModel() {
private val _isBottomBarEnabled = MutableLiveData(true)
val isBottomBarEnabled: LiveData<Boolean>
get() = _isBottomBarEnabled

fun enableBottomBar(enable: Boolean) {
_isBottomBarEnabled.value = enable
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.openedx.app.data.networking

import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import org.openedx.app.BuildConfig
import org.openedx.core.system.notifier.AppUpgradeEvent
import org.openedx.core.system.notifier.AppUpgradeNotifier
import org.openedx.core.utils.TimeUtils
import java.util.Date

class AppUpgradeInterceptor(
private val appUpgradeNotifier: AppUpgradeNotifier
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
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
runBlocking {
when {
responseCode == 426 -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
}

BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime > Date().time -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRecommendedEvent(latestAppVersion))
}

latestAppVersion.isNotEmpty() && BuildConfig.VERSION_NAME != latestAppVersion && lastSupportedDateTime < Date().time -> {
appUpgradeNotifier.send(AppUpgradeEvent.UpgradeRequiredEvent)
}
}
}
return response
}

companion object {
const val HEADER_APP_LATEST_VERSION = "EDX-APP-LATEST-VERSION"
const val HEADER_APP_VERSION_LAST_SUPPORTED_DATE = "EDX-APP-VERSION-LAST-SUPPORTED-DATE"
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class HandleErrorInterceptor(
return response
}
}
} else if (errorResponse.errorDescription != null) {
} else if (errorResponse?.errorDescription != null) {
throw EdxError.ValidationException(errorResponse.errorDescription ?: "")
}
} catch (e: JsonSyntaxException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.openedx.app.data.networking

import android.content.Context
import okhttp3.Interceptor
import okhttp3.Response
import org.openedx.core.data.storage.CorePreferences
import org.openedx.app.BuildConfig
import org.openedx.core.BuildConfig.ACCESS_TOKEN_TYPE
import org.openedx.core.data.storage.CorePreferences

class HeadersInterceptor(private val preferencesManager: CorePreferences) : Interceptor {
class HeadersInterceptor(private val context: Context, private val preferencesManager: CorePreferences) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response = chain.run {
proceed(
Expand All @@ -18,6 +20,12 @@ class HeadersInterceptor(private val preferencesManager: CorePreferences) : Inte
}

addHeader("Accept", "application/json")
addHeader(
"User-Agent", System.getProperty("http.agent") + " " +
context.getString(org.openedx.core.R.string.app_name) + "/" +
BuildConfig.APPLICATION_ID + "/" +
BuildConfig.VERSION_NAME
)
}.build()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
putString(key, value)
}.apply()
}

private fun getString(key: String): String = sharedPreferences.getString(key, "") ?: ""

override fun clear() {
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import org.koin.android.ext.koin.androidApplication
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.openedx.core.data.storage.CorePreferences
import org.openedx.core.presentation.global.app_upgrade.AppUpgradeRouter
import org.openedx.core.system.notifier.AppUpgradeNotifier
import org.openedx.profile.data.storage.ProfilePreferences
import org.openedx.whatsnew.WhatsNewFileManager
import org.openedx.whatsnew.WhatsNewRouter
Expand All @@ -59,6 +61,7 @@ val appModule = module {
single { CourseNotifier() }
single { DiscussionNotifier() }
single { ProfileNotifier() }
single { AppUpgradeNotifier() }

single { AppRouter() }
single<AuthRouter> { get<AppRouter>() }
Expand All @@ -68,6 +71,7 @@ val appModule = module {
single<DiscussionRouter> { get<AppRouter>() }
single<ProfileRouter> { get<AppRouter>() }
single<WhatsNewRouter> { get<AppRouter>() }
single<AppUpgradeRouter> { get<AppRouter>() }


single { NetworkConnection(get()) }
Expand Down
20 changes: 11 additions & 9 deletions app/src/main/java/org/openedx/app/di/NetworkingModule.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package org.openedx.app.di

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.koin.dsl.module
import org.openedx.app.data.networking.AppUpgradeInterceptor
import org.openedx.app.data.networking.HandleErrorInterceptor
import org.openedx.app.data.networking.HeadersInterceptor
import org.openedx.app.data.networking.OauthRefreshTokenAuthenticator
import org.openedx.auth.data.api.AuthApi
import org.openedx.core.BuildConfig
import org.openedx.core.data.api.CookiesApi
import org.openedx.core.data.api.CourseApi
import org.openedx.discussion.data.api.DiscussionApi
import org.openedx.app.data.networking.HandleErrorInterceptor
import org.openedx.app.data.networking.HeadersInterceptor
import org.openedx.app.data.networking.OauthRefreshTokenAuthenticator
import org.openedx.profile.data.api.ProfileApi
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.koin.dsl.module
import org.openedx.core.BuildConfig
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
Expand All @@ -24,18 +25,19 @@ val networkingModule = module {
OkHttpClient.Builder().apply {
writeTimeout(60, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
addInterceptor(HeadersInterceptor(get()))
addInterceptor(HeadersInterceptor(get(), get()))
if (BuildConfig.DEBUG) {
addNetworkInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
}
addInterceptor(HandleErrorInterceptor(get()))
addInterceptor(AppUpgradeInterceptor(get()))
authenticator(get<OauthRefreshTokenAuthenticator>())
}.build()
}

single<Retrofit> {
Retrofit.Builder()
.baseUrl(org.openedx.core.BuildConfig.BASE_URL)
.baseUrl(BuildConfig.BASE_URL)
.client(get())
.addConverterFactory(GsonConverterFactory.create(get()))
.build()
Expand Down
Loading