Skip to content
Open
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
252 changes: 80 additions & 172 deletions app/schemas/org.openedx.app.room.AppDatabase/4.json

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${applicationId}" />
</intent-filter>

<!-- Branch URI Scheme -->
<intent-filter>
Expand Down
8 changes: 2 additions & 6 deletions app/src/main/java/org/openedx/app/AppActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,8 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
WindowCompat.setDecorFitsSystemWindows(this, false)
val insetsController = WindowInsetsControllerCompat(this, binding.root)
insetsController.isAppearanceLightStatusBars = !isUsingNightModeResources()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
insetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else {
window.statusBarColor = Color.TRANSPARENT
}
insetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}

Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/org/openedx/app/AppRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.openedx.core.CalendarRouter
import org.openedx.core.FragmentViewType
import org.openedx.core.presentation.global.appupgrade.AppUpgradeRouter
import org.openedx.core.presentation.global.appupgrade.UpgradeRequiredFragment
import org.openedx.core.presentation.global.webview.SSOWebContentFragment
import org.openedx.core.presentation.global.webview.WebContentFragment
import org.openedx.core.presentation.settings.video.VideoQualityFragment
import org.openedx.core.presentation.settings.video.VideoQualityType
Expand Down Expand Up @@ -432,6 +433,13 @@ class AppRouter :
)
}

override fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String) {
replaceFragmentWithBackStack(
fm,
SSOWebContentFragment.newInstance(title = title, url = url)
)
}

override fun navigateToManageAccount(fm: FragmentManager) {
replaceFragmentWithBackStack(fm, ManageAccountFragment())
}
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import org.openedx.app.room.DatabaseManager
import org.openedx.auth.presentation.AgreementProvider
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.AuthRouter
import org.openedx.auth.presentation.sso.BrowserAuthHelper
import org.openedx.auth.presentation.sso.FacebookAuthHelper
import org.openedx.auth.presentation.sso.GoogleAuthHelper
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
Expand Down Expand Up @@ -215,7 +214,6 @@ val appModule = module {
factory { FacebookAuthHelper() }
factory { GoogleAuthHelper(get()) }
factory { MicrosoftAuthHelper() }
factory { BrowserAuthHelper(get()) }
factory { OAuthHelper(get(), get(), get()) }
factory { VideoPreviewHelper(get(), get()) }

Expand Down
7 changes: 4 additions & 3 deletions app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import org.openedx.profile.presentation.profile.ProfileViewModel
import org.openedx.profile.presentation.settings.SettingsViewModel
import org.openedx.profile.presentation.video.VideoSettingsViewModel
import org.openedx.whatsnew.presentation.whatsnew.WhatsNewViewModel
import android.content.res.Resources

val screenModule = module {

Expand Down Expand Up @@ -108,11 +109,11 @@ val screenModule = module {
get(),
get(),
get(),
get(),
)
}

viewModel { (courseId: String?, infoType: String?, authCode: String) ->
val lang = Resources.getSystem().configuration.locales[0].language
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lang is lowercased here but in
SSO_BUTTON_TITLE:
AR: "الدخول عبر SSO"
EN: "Sign in with SSO"

parameters are uppercased. Can’t check this because in current implementation we are ignoring SSO_BUTTON_TITLE

viewModel { (courseId: String?, infoType: String?) ->
SignInViewModel(
get(),
get(),
Expand All @@ -130,7 +131,7 @@ val screenModule = module {
get(),
courseId,
infoType,
authCode,
lang
)
}

Expand Down
11 changes: 0 additions & 11 deletions auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,6 @@ interface AuthApi {
@Field("asymmetric_jwt") isAsymmetricJwt: Boolean = true,
): AuthResponse

@FormUrlEncoded
@POST(ApiConstants.URL_ACCESS_TOKEN)
suspend fun getAccessTokenFromCode(
@Field("grant_type") grantType: String,
@Field("client_id") clientId: String,
@Field("code") code: String,
@Field("redirect_uri") redirectUri: String,
@Field("token_type") tokenType: String,
@Field("asymmetric_jwt") isAsymmetricJwt: Boolean = true,
): AuthResponse

@FormUrlEncoded
@POST(ApiConstants.URL_ACCESS_TOKEN)
fun refreshAccessToken(
Expand Down
1 change: 0 additions & 1 deletion auth/src/main/java/org/openedx/auth/data/model/AuthType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ enum class AuthType(val postfix: String, val methodName: String) {
GOOGLE(ApiConstants.AUTH_TYPE_GOOGLE, "Google"),
FACEBOOK(ApiConstants.AUTH_TYPE_FB, "Facebook"),
MICROSOFT(ApiConstants.AUTH_TYPE_MICROSOFT, "Microsoft"),
BROWSER(ApiConstants.AUTH_TYPE_BROWSER, "Browser")
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
.processAuthResponse()
}

suspend fun ssoLogin(
jwtToken: String
) {
if (preferencesManager.accessToken.isBlank()){

Check warning

Code scanning / detekt

Reports spaces around curly braces Warning

Missing spacing before "{"
preferencesManager.accessToken = jwtToken
}
val user = api.getProfile()
preferencesManager.user = user
}

suspend fun socialLogin(token: String?, authType: AuthType) {
require(!token.isNullOrBlank()) { "Token is null" }
api.exchangeAccessToken(
Expand All @@ -43,16 +53,6 @@
.processAuthResponse()
}

suspend fun browserAuthCodeLogin(code: String) {
api.getAccessTokenFromCode(
grantType = ApiConstants.GRANT_TYPE_CODE,
clientId = config.getOAuthClientId(),
code = code,
redirectUri = "${config.getAppId()}://${ApiConstants.BrowserLogin.REDIRECT_HOST}",
tokenType = config.getAccessTokenType(),
).mapToDomain().processAuthResponse()
}

suspend fun getRegistrationFields(): List<RegistrationField> {
return api.getRegistrationFields().fields?.map { it.mapToDomain() } ?: emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class AuthInteractor(private val repository: AuthRepository) {
repository.login(username, password)
}

suspend fun loginSocial(token: String?, authType: AuthType) {
repository.socialLogin(token, authType)
suspend fun ssoLogin(
jwtToken: String
) {
repository.ssoLogin(jwtToken)
}

suspend fun loginAuthCode(authCode: String) {
repository.browserAuthCodeLogin(authCode)
suspend fun loginSocial(token: String?, authType: AuthType) {
repository.socialLogin(token, authType)
}

suspend fun getRegistrationFields(): List<RegistrationField> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ interface AuthRouter {

fun navigateToWebContent(fm: FragmentManager, title: String, url: String)

fun navigateToSSOWebContent(fm: FragmentManager, title: String, url: String)

fun clearBackStack(fm: FragmentManager)
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import androidx.fragment.app.Fragment
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.openedx.auth.R
import org.openedx.core.ApiConstants
import org.openedx.core.ui.AuthButtonsPanel
import org.openedx.core.ui.SearchBar
import org.openedx.core.ui.displayCutoutForLandscape
Expand All @@ -51,7 +50,6 @@ import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appTypography
import org.openedx.core.ui.theme.compose.LogistrationLogoView
import org.openedx.foundation.utils.UrlUtils

class LogistrationFragment : Fragment() {

Expand All @@ -69,22 +67,10 @@ class LogistrationFragment : Fragment() {
OpenEdXTheme {
LogistrationScreen(
onSignInClick = {
if (viewModel.isBrowserLoginEnabled) {
viewModel.signInBrowser(requireActivity())
} else {
viewModel.navigateToSignIn(parentFragmentManager)
}
viewModel.navigateToSignIn(parentFragmentManager)
},
onRegisterClick = {
if (viewModel.isBrowserRegistrationEnabled) {
UrlUtils.openInBrowser(
activity = context,
apiHostUrl = viewModel.apiHostUrl,
url = ApiConstants.URL_REGISTER_BROWSER,
)
} else {
viewModel.navigateToSignUp(parentFragmentManager)
}
viewModel.navigateToSignUp(parentFragmentManager)
},
onSearchClick = { querySearch ->
viewModel.navigateToDiscovery(parentFragmentManager, querySearch)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package org.openedx.auth.presentation.logistration

import android.app.Activity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.AuthAnalyticsEvent
import org.openedx.auth.presentation.AuthAnalyticsKey
import org.openedx.auth.presentation.AuthRouter
import org.openedx.auth.presentation.sso.BrowserAuthHelper
import org.openedx.core.config.Config
import org.openedx.core.utils.Logger
import org.openedx.foundation.extension.takeIfNotEmpty
import org.openedx.foundation.presentation.BaseViewModel

Expand All @@ -19,16 +14,10 @@ class LogistrationViewModel(
private val router: AuthRouter,
private val config: Config,
private val analytics: AuthAnalytics,
private val browserAuthHelper: BrowserAuthHelper,
) : BaseViewModel() {

private val logger = Logger("LogistrationViewModel")

private val discoveryTypeWebView get() = config.getDiscoveryConfig().isViewTypeWebView()
val isRegistrationEnabled get() = config.isRegistrationEnabled()
val isBrowserRegistrationEnabled get() = config.isBrowserRegistrationEnabled()
val isBrowserLoginEnabled get() = config.isBrowserLoginEnabled()
val apiHostUrl get() = config.getApiHostURL()

init {
logLogistrationScreenEvent()
Expand All @@ -39,16 +28,6 @@ class LogistrationViewModel(
logEvent(AuthAnalyticsEvent.SIGN_IN_CLICKED)
}

fun signInBrowser(activityContext: Activity) {
viewModelScope.launch {
runCatching {
browserAuthHelper.signIn(activityContext)
}.onFailure {
logger.e { "Browser auth error: $it" }
}
}
}

fun navigateToSignUp(parentFragmentManager: FragmentManager) {
router.navigateToSignUp(parentFragmentManager, courseId, null)
logEvent(AuthAnalyticsEvent.REGISTER_CLICKED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -11,6 +12,7 @@
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResultListener
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.openedx.auth.data.model.AuthType
Expand All @@ -25,8 +27,7 @@
private val viewModel: SignInViewModel by viewModel {
parametersOf(
requireArguments().getString(ARG_COURSE_ID, ""),
requireArguments().getString(ARG_INFO_TYPE, ""),
requireArguments().getString(ARG_AUTH_CODE, ""),
requireArguments().getString(ARG_INFO_TYPE, "")
)
}

Expand All @@ -44,16 +45,15 @@
val appUpgradeEvent by viewModel.appUpgradeEvent.observeAsState(null)

if (appUpgradeEvent == null) {
if (viewModel.authCode != "" && !state.loginFailure && !state.loginSuccess) {
viewModel.signInAuthCode(viewModel.authCode)
}

LoginScreen(

Check warning

Code scanning / detekt

Reports methods that have an empty first line. Warning

First line in a method block should not be empty
windowSize = windowSize,
state = state,
uiMessage = uiMessage,
onEvent = { event ->
when (event) {
is AuthEvent.SignIn -> viewModel.login(event.login, event.password)
is AuthEvent.SsoSignIn -> viewModel.ssoClicked(parentFragmentManager)
is AuthEvent.SocialSignIn -> viewModel.socialAuth(
this@SignInFragment,
event.authType
Expand All @@ -63,10 +63,6 @@
viewModel.navigateToForgotPassword(parentFragmentManager)
}

AuthEvent.SignInBrowser -> {
viewModel.signInBrowser(requireActivity())
}

AuthEvent.RegisterClick -> {
viewModel.navigateToSignUp(parentFragmentManager)
}
Expand Down Expand Up @@ -97,16 +93,21 @@
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setFragmentResultListener("requestKey") { _, bundle ->
val token = bundle.getString("bundleKey")
viewModel.ssoLogin(token = "$token")
}
}
companion object {
private const val ARG_COURSE_ID = "courseId"
private const val ARG_INFO_TYPE = "info_type"
private const val ARG_AUTH_CODE = "auth_code"
fun newInstance(courseId: String?, infoType: String?, authCode: String? = null): SignInFragment {
fun newInstance(courseId: String?, infoType: String?): SignInFragment {
val fragment = SignInFragment()
fragment.arguments = bundleOf(
ARG_COURSE_ID to courseId,
ARG_INFO_TYPE to infoType,
ARG_AUTH_CODE to authCode,
ARG_INFO_TYPE to infoType
)
return fragment
}
Expand All @@ -115,9 +116,9 @@

internal sealed interface AuthEvent {
data class SignIn(val login: String, val password: String) : AuthEvent
data class SsoSignIn(val jwtToken: String) : AuthEvent
data class SocialSignIn(val authType: AuthType) : AuthEvent
data class OpenLink(val links: Map<String, String>, val link: String) : AuthEvent
object SignInBrowser : AuthEvent
object RegisterClick : AuthEvent
object ForgotPasswordClick : AuthEvent
object BackClick : AuthEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ import org.openedx.core.domain.model.RegistrationField
* @param loginSuccess is login succeed
*/
internal data class SignInUIState(
val isLoginRegistrationFormEnabled: Boolean = true,
val isSSOLoginEnabled: Boolean = false,
val ssoButtonTitle: String = "",
val isSSODefaultLoginButton: Boolean = false,
val isFacebookAuthEnabled: Boolean = false,
val isGoogleAuthEnabled: Boolean = false,
val isMicrosoftAuthEnabled: Boolean = false,
val isSocialAuthEnabled: Boolean = false,
val isBrowserLoginEnabled: Boolean = false,
val isBrowserRegistrationEnabled: Boolean = false,
val isLogistrationEnabled: Boolean = false,
val isRegistrationEnabled: Boolean = true,
val showProgress: Boolean = false,
val loginSuccess: Boolean = false,
val agreement: RegistrationField? = null,
val loginFailure: Boolean = false,
)
Loading
Loading