Skip to content

Commit

Permalink
Merge pull request #11 from oppia/develop
Browse files Browse the repository at this point in the history
Develop (fork) branch update
  • Loading branch information
subhajitxyz authored Dec 11, 2024
2 parents b1ca8e1 + f4a4a47 commit ad7e380
Show file tree
Hide file tree
Showing 87 changed files with 3,228 additions and 470 deletions.
1 change: 1 addition & 0 deletions app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ android_library(
"//third_party:androidx_databinding_databinding-common",
"//third_party:androidx_databinding_databinding-runtime",
"//third_party:androidx_drawerlayout_drawerlayout",
"//third_party:androidx_fragment_fragment",
"//third_party:androidx_lifecycle_lifecycle-livedata-core",
"//third_party:androidx_recyclerview_recyclerview",
"//third_party:androidx_viewpager2_viewpager2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity
import org.oppia.android.app.activity.route.ActivityRouter
import org.oppia.android.app.drawer.ExitProfileDialogFragment
import org.oppia.android.app.drawer.TAG_SWITCH_PROFILE_DIALOG
import org.oppia.android.app.home.ExitProfileListener
import org.oppia.android.app.home.RouteToRecentlyPlayedListener
import org.oppia.android.app.home.RouteToTopicListener
import org.oppia.android.app.home.RouteToTopicPlayStoryListener
import org.oppia.android.app.model.DestinationScreen
import org.oppia.android.app.model.ExitProfileDialogArguments
import org.oppia.android.app.model.HighlightItem
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.ProfileType
import org.oppia.android.app.model.RecentlyPlayedActivityParams
import org.oppia.android.app.model.RecentlyPlayedActivityTitle
import org.oppia.android.app.model.ScreenName.CLASSROOM_LIST_ACTIVITY
import org.oppia.android.app.topic.TopicActivity.Companion.createTopicActivityIntent
import org.oppia.android.app.topic.TopicActivity.Companion.createTopicPlayStoryActivityIntent
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName
import org.oppia.android.util.platformparameter.EnableOnboardingFlowV2
import org.oppia.android.util.platformparameter.PlatformParameterValue
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId
import javax.inject.Inject
Expand All @@ -32,7 +36,8 @@ class ClassroomListActivity :
InjectableAutoLocalizedAppCompatActivity(),
RouteToTopicListener,
RouteToTopicPlayStoryListener,
RouteToRecentlyPlayedListener {
RouteToRecentlyPlayedListener,
ExitProfileListener {
@Inject
lateinit var classroomListActivityPresenter: ClassroomListActivityPresenter

Expand All @@ -44,6 +49,10 @@ class ClassroomListActivity :

private var internalProfileId: Int = -1

@Inject
@field:EnableOnboardingFlowV2
lateinit var enableOnboardingFlowV2: PlatformParameterValue<Boolean>

companion object {
/** Returns a new [Intent] to route to [ClassroomListActivity] for a specified [profileId]. */
fun createClassroomListActivity(context: Context, profileId: ProfileId?): Intent {
Expand All @@ -63,11 +72,6 @@ class ClassroomListActivity :
title = resourceHandler.getStringInLocale(R.string.classroom_list_activity_title)
}

override fun onRestart() {
super.onRestart()
classroomListActivityPresenter.handleOnRestart()
}

override fun onBackPressed() {
val previousFragment =
supportFragmentManager.findFragmentByTag(TAG_SWITCH_PROFILE_DIALOG)
Expand Down Expand Up @@ -121,4 +125,24 @@ class ClassroomListActivity :
)
)
}

override fun exitProfile(profileType: ProfileType) {
val previousFragment =
supportFragmentManager.findFragmentByTag(TAG_SWITCH_PROFILE_DIALOG)
if (previousFragment != null) {
supportFragmentManager.beginTransaction().remove(previousFragment).commitNow()
}
val exitProfileDialogArguments =
ExitProfileDialogArguments
.newBuilder().apply {
if (enableOnboardingFlowV2.value) {
this.profileType = profileType
}
this.highlightItem = HighlightItem.NONE
}
.build()
val dialogFragment = ExitProfileDialogFragment
.newInstance(exitProfileDialogArguments = exitProfileDialogArguments)
dialogFragment.showNow(supportFragmentManager, TAG_SWITCH_PROFILE_DIALOG)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ class ClassroomListActivityPresenter @Inject constructor(private val activity: A
}
}

/** Handles the activity restart. Re-initializes the navigation drawer. */
fun handleOnRestart() {
setUpNavigationDrawer()
}

private fun setUpNavigationDrawer() {
val toolbar = activity.findViewById<View>(R.id.classroom_list_activity_toolbar) as Toolbar
activity.setSupportActionBar(toolbar)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.oppia.android.app.classroom
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
Expand Down Expand Up @@ -36,6 +37,7 @@ import org.oppia.android.app.classroom.promotedlist.PromotedStoryList
import org.oppia.android.app.classroom.topiclist.AllTopicsHeaderText
import org.oppia.android.app.classroom.topiclist.TopicCard
import org.oppia.android.app.classroom.welcome.WelcomeText
import org.oppia.android.app.home.ExitProfileListener
import org.oppia.android.app.home.HomeItemViewModel
import org.oppia.android.app.home.RouteToTopicPlayStoryListener
import org.oppia.android.app.home.WelcomeViewModel
Expand All @@ -48,6 +50,8 @@ import org.oppia.android.app.home.topiclist.TopicSummaryViewModel
import org.oppia.android.app.model.ClassroomSummary
import org.oppia.android.app.model.LessonThumbnail
import org.oppia.android.app.model.LessonThumbnailGraphic
import org.oppia.android.app.model.Profile
import org.oppia.android.app.model.ProfileType
import org.oppia.android.app.model.TopicSummary
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.utility.datetime.DateTimeUtil
Expand All @@ -59,9 +63,13 @@ import org.oppia.android.domain.oppialogger.analytics.AnalyticsController
import org.oppia.android.domain.profile.ProfileManagementController
import org.oppia.android.domain.topic.TopicListController
import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.data.AsyncResult
import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import org.oppia.android.util.locale.OppiaLocale
import org.oppia.android.util.parser.html.StoryHtmlParserEntityType
import org.oppia.android.util.parser.html.TopicHtmlParserEntityType
import org.oppia.android.util.platformparameter.EnableOnboardingFlowV2
import org.oppia.android.util.platformparameter.PlatformParameterValue
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId
import javax.inject.Inject

Expand All @@ -82,14 +90,18 @@ class ClassroomListFragmentPresenter @Inject constructor(
private val dateTimeUtil: DateTimeUtil,
private val translationController: TranslationController,
private val machineLocale: OppiaLocale.MachineLocale,
private val appStartupStateController: AppStartupStateController,
private val analyticsController: AnalyticsController,
@EnableOnboardingFlowV2
private val enableOnboardingFlowV2: PlatformParameterValue<Boolean>,
private val appStartupStateController: AppStartupStateController
) {
private val routeToTopicPlayStoryListener = activity as RouteToTopicPlayStoryListener
private val exitProfileListener = activity as ExitProfileListener
private lateinit var binding: ClassroomListFragmentBinding
private lateinit var classroomListViewModel: ClassroomListViewModel
private var internalProfileId: Int = -1
private val profileId = activity.intent.extractCurrentUserProfileId()
private var onBackPressedCallback: OnBackPressedCallback? = null

/** Creates and returns the view for the [ClassroomListFragment]. */
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
Expand Down Expand Up @@ -155,6 +167,10 @@ class ClassroomListFragmentPresenter @Inject constructor(
}
)

profileManagementController.getProfile(profileId).toLiveData().observe(fragment) {
processProfileResult(it)
}

return binding.root
}

Expand Down Expand Up @@ -190,26 +206,25 @@ class ClassroomListFragmentPresenter @Inject constructor(
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ClassroomListScreen() {
val groupedItems = classroomListViewModel.homeItemViewModelListLiveData.value
?.plus(classroomListViewModel.topicList)
?.groupBy { it::class }
val groupedItems = (
classroomListViewModel.homeItemViewModelListLiveData.value.orEmpty() +
classroomListViewModel.topicList
)
.groupBy { it::class }
val topicListSpanCount = integerResource(id = R.integer.home_span_count)
val listState = rememberLazyListState()
val classroomListIndex = groupedItems
?.flatMap { (type, items) -> items.map { type to it } }
?.indexOfFirst { it.first == AllClassroomsViewModel::class }
?: -1
.flatMap { (type, items) -> items.map { type to it } }
.indexOfFirst { it.first == AllClassroomsViewModel::class }

LazyColumn(
modifier = Modifier.testTag(CLASSROOM_LIST_SCREEN_TEST_TAG),
state = listState
) {
groupedItems?.forEach { (type, items) ->
groupedItems.forEach { (type, items) ->
when (type) {
WelcomeViewModel::class -> items.forEach { item ->
item {
WelcomeText(welcomeViewModel = item as WelcomeViewModel)
}
item { WelcomeText(welcomeViewModel = item as WelcomeViewModel) }
}
PromotedStoryListViewModel::class -> items.forEach { item ->
item {
Expand All @@ -223,26 +238,22 @@ class ClassroomListFragmentPresenter @Inject constructor(
item {
ComingSoonTopicList(
comingSoonTopicListViewModel = item as ComingSoonTopicListViewModel,
machineLocale = machineLocale,
machineLocale = machineLocale
)
}
}
AllClassroomsViewModel::class -> items.forEach { _ ->
item {
AllClassroomsHeaderText()
}
item { AllClassroomsHeaderText() }
}
ClassroomSummaryViewModel::class -> stickyHeader() {
ClassroomSummaryViewModel::class -> stickyHeader {
ClassroomList(
classroomSummaryList = items.map { it as ClassroomSummaryViewModel },
selectedClassroomId = classroomListViewModel.selectedClassroomId.get() ?: "",
selectedClassroomId = classroomListViewModel.selectedClassroomId.get().orEmpty(),
isSticky = listState.firstVisibleItemIndex >= classroomListIndex
)
}
AllTopicsViewModel::class -> items.forEach { _ ->
item {
AllTopicsHeaderText()
}
item { AllTopicsHeaderText() }
}
TopicSummaryViewModel::class -> {
gridItems(
Expand All @@ -259,12 +270,61 @@ class ClassroomListFragmentPresenter @Inject constructor(
}
}

private fun processProfileResult(result: AsyncResult<Profile>) {
when (result) {
is AsyncResult.Success -> {
val profile = result.value
val profileType = profile.profileType

if (enableOnboardingFlowV2.value && !profile.completedProfileOnboarding) {
// These asynchronous API calls do not block or wait for their results. They execute in
// the background and have minimal chances of interfering with the synchronous
// `handleBackPress` call below.
profileManagementController.markProfileOnboardingEnded(profileId)
if (profileType == ProfileType.SOLE_LEARNER || profileType == ProfileType.SUPERVISOR) {
appStartupStateController.markOnboardingFlowCompleted(profileId)
}
}

// This synchronous function call executes independently of the async calls above.
handleBackPress(profileType)
}
is AsyncResult.Failure -> {
oppiaLogger.e(
"ClassroomListFragment", "Failed to fetch profile with id:$profileId", result.error
)
Profile.getDefaultInstance()
}
is AsyncResult.Pending -> {
Profile.getDefaultInstance()
}
}
}

private fun logHomeActivityEvent() {
analyticsController.logImportantEvent(
oppiaLogger.createOpenHomeContext(),
profileId
)
}

private fun handleBackPress(profileType: ProfileType) {
onBackPressedCallback?.remove()

onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
exitProfileListener.exitProfile(profileType)
// The dispatcher can hold a reference to the host
// so we need to null it out to prevent memory leaks.
this.remove()
onBackPressedCallback = null
}
}

onBackPressedCallback?.let { callback ->
activity.onBackPressedDispatcher.addCallback(fragment, callback)
}
}
}

/** Adds a grid of items to a LazyListScope with specified arrangement and item content. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import android.view.KeyEvent.ACTION_UP
import android.view.KeyEvent.KEYCODE_BACK
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.appcompat.widget.AppCompatEditText
import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
import org.oppia.android.app.utility.KeyboardHelper.Companion.hideSoftKeyboard
import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
Expand All @@ -20,19 +20,19 @@ import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
// background="@drawable/edit_text_background"
// maxLength="200".

/** The custom EditText class for fraction input interaction view. */
/** The custom AppCompatEditText class for fraction input interaction view. */
class FractionInputInteractionView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = android.R.attr.editTextStyle
) : EditText(context, attrs, defStyle), View.OnFocusChangeListener {
) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
private var hintText: CharSequence = ""
private val stateKeyboardButtonListener: StateKeyboardButtonListener

init {
onFocusChangeListener = this
// Assume multi-line for the purpose of properly showing long hints.
setSingleLine(hint != null)
isSingleLine = hint != null
stateKeyboardButtonListener = context as StateKeyboardButtonListener
}

Expand Down Expand Up @@ -64,12 +64,12 @@ class FractionInputInteractionView @JvmOverloads constructor(
private fun hideHint() {
hint = ""
typeface = Typeface.DEFAULT
setSingleLine(true)
isSingleLine = true
}

private fun restoreHint() {
hint = hintText
if (text.isEmpty()) setTypeface(typeface, Typeface.ITALIC)
setSingleLine(false)
if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
isSingleLine = false
}
}
Loading

0 comments on commit ad7e380

Please sign in to comment.