Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/oppia/oppia-android into o…
Browse files Browse the repository at this point in the history
…ppia#4419

� Conflicts:
�	utility/src/main/java/org/oppia/android/util/logging/KenyaAlphaEventTypeToHumanReadableNameConverterImpl.kt
�	utility/src/test/java/org/oppia/android/util/logging/KenyaAlphaEventBundleCreatorTest.kt
  • Loading branch information
XichengSpencer committed Jul 1, 2024
2 parents 2508f35 + b5354f9 commit 3c5236b
Show file tree
Hide file tree
Showing 77 changed files with 6,012 additions and 86 deletions.
1 change: 1 addition & 0 deletions app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ VIEW_MODELS_WITH_RESOURCE_IMPORTS = [
"src/main/java/org/oppia/android/app/home/recentlyplayed/PromotedStoryViewModel.kt",
"src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedViewModel.kt",
"src/main/java/org/oppia/android/app/home/topiclist/TopicSummaryViewModel.kt",
"src/main/java/org/oppia/android/app/onboarding/CreateProfileViewModel.kt",
"src/main/java/org/oppia/android/app/onboarding/OnboadingSlideViewModel.kt",
"src/main/java/org/oppia/android/app/onboarding/OnboardingViewModel.kt",
"src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicItemViewModel.kt",
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,18 @@
android:name=".app.classroom.ClassroomListActivity"
android:label="@string/classroom_list_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />

<activity
android:name=".app.onboarding.OnboardingProfileTypeActivity"
android:label="@string/onboarding_profile_type_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.onboarding.CreateProfileActivity"
android:label="@string/create_profile_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.onboarding.IntroActivity"
android:label="@string/onboarding_learner_intro_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListActivity
import org.oppia.android.app.home.HomeActivity
import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedActivity
import org.oppia.android.app.mydownloads.MyDownloadsActivity
import org.oppia.android.app.onboarding.CreateProfileActivity
import org.oppia.android.app.onboarding.IntroActivity
import org.oppia.android.app.onboarding.OnboardingActivity
import org.oppia.android.app.onboarding.OnboardingProfileTypeActivity
import org.oppia.android.app.ongoingtopiclist.OngoingTopicListActivity
import org.oppia.android.app.options.AppLanguageActivity
import org.oppia.android.app.options.AudioLanguageActivity
Expand Down Expand Up @@ -220,4 +223,7 @@ interface ActivityComponentImpl :
fun inject(surveyActivity: SurveyActivity)
fun inject(colorBindingAdaptersTestActivity: ColorBindingAdaptersTestActivity)
fun inject(classroomListActivity: ClassroomListActivity)
fun inject(onboardingProfileTypeActivity: OnboardingProfileTypeActivity)
fun inject(createProfileActivity: CreateProfileActivity)
fun inject(introActivity: IntroActivity)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import org.oppia.android.app.notice.ForcedAppDeprecationNoticeDialogFragment
import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragment
import org.oppia.android.app.notice.OptionalAppDeprecationNoticeDialogFragment
import org.oppia.android.app.notice.OsDeprecationNoticeDialogFragment
import org.oppia.android.app.onboarding.CreateProfileFragment
import org.oppia.android.app.onboarding.IntroFragment
import org.oppia.android.app.onboarding.OnboardingFragment
import org.oppia.android.app.onboarding.OnboardingProfileTypeFragment
import org.oppia.android.app.ongoingtopiclist.OngoingTopicListFragment
import org.oppia.android.app.options.AppLanguageFragment
import org.oppia.android.app.options.AudioLanguageFragment
Expand Down Expand Up @@ -196,4 +199,7 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto
fun inject(surveyWelcomeDialogFragment: SurveyWelcomeDialogFragment)
fun inject(surveyOutroDialogFragment: SurveyOutroDialogFragment)
fun inject(classroomListFragment: ClassroomListFragment)
fun inject(onboardingProfileTypeFragment: OnboardingProfileTypeFragment)
fun inject(createProfileFragment: CreateProfileFragment)
fun inject(introFragment: IntroFragment)
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,15 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
binding.expandableHintHeader.setOnClickListener {
if (hintViewModel.isHintRevealed.get()) {
expandOrCollapseItem(position)
if (position in expandedItemIndexes)
(fragment.requireActivity() as? ViewHintListener)?.viewHint(hintIndex = position)
}
}
binding.expandHintListIcon.setOnClickListener {
if (hintViewModel.isHintRevealed.get()) {
expandOrCollapseItem(position)
if (position in expandedItemIndexes)
(fragment.requireActivity() as? ViewHintListener)?.viewHint(hintIndex = position)
}
}

Expand Down Expand Up @@ -262,11 +266,15 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
binding.expandableSolutionHeader.setOnClickListener {
if (solutionViewModel.isSolutionRevealed.get()) {
expandOrCollapseItem(position)
if (position in expandedItemIndexes)
(fragment.requireActivity() as? ViewSolutionInterface)?.viewSolution()
}
}
binding.expandSolutionListIcon.setOnClickListener {
if (solutionViewModel.isSolutionRevealed.get()) {
expandOrCollapseItem(position)
if (position in expandedItemIndexes)
(fragment.requireActivity() as? ViewSolutionInterface)?.viewSolution()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.oppia.android.app.hintsandsolution

/** Callback listener for when the user wishes to view a hint. */
interface ViewHintListener {
/**
* Called when the user indicates they want to view the hint corresponding to the specified
* index.
*/
fun viewHint(hintIndex: Int)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.oppia.android.app.hintsandsolution

/** Interface to check the preference regarding alert for [HintsAndSolutionDialogFragment]. */
interface ViewSolutionInterface {
/**
* Called when the user indicates they want to view the solution.
*/
fun viewSolution()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.oppia.android.app.onboarding

import android.content.Context
import android.content.Intent
import android.os.Bundle
import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity
import org.oppia.android.app.model.ScreenName.CREATE_PROFILE_ACTIVITY
import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName
import javax.inject.Inject

/** Activity for displaying a new learner profile creation flow. */
class CreateProfileActivity : InjectableAutoLocalizedAppCompatActivity() {
@Inject
lateinit var learnerProfileActivityPresenter: CreateProfileActivityPresenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)

learnerProfileActivityPresenter.handleOnCreate()
}

companion object {
/** Returns a new [Intent] open a [CreateProfileActivity] with the specified params. */
fun createProfileActivityIntent(context: Context): Intent {
return Intent(context, CreateProfileActivity::class.java).apply {
decorateWithScreenName(CREATE_PROFILE_ACTIVITY)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.oppia.android.app.onboarding

import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import org.oppia.android.R
import org.oppia.android.databinding.CreateProfileActivityBinding
import javax.inject.Inject

private const val TAG_CREATE_PROFILE_ACTIVITY_FRAGMENT = "TAG_CREATE_PROFILE_ACTIVITY_FRAGMENT"

/** Presenter for [CreateProfileActivity]. */
class CreateProfileActivityPresenter @Inject constructor(
private val activity: AppCompatActivity
) {
private lateinit var binding: CreateProfileActivityBinding

/** Handle creation and binding of the CreateProfileActivity layout. */
fun handleOnCreate() {
binding = DataBindingUtil.setContentView(activity, R.layout.create_profile_activity)
binding.apply {
lifecycleOwner = activity
}

if (getNewLearnerProfileFragment() == null) {
val createLearnerProfileFragment = CreateProfileFragment()
activity.supportFragmentManager.beginTransaction().add(
R.id.profile_fragment_placeholder,
createLearnerProfileFragment,
TAG_CREATE_PROFILE_ACTIVITY_FRAGMENT
).commitNow()
}
}

private fun getNewLearnerProfileFragment(): CreateProfileFragment? {
return activity.supportFragmentManager.findFragmentByTag(
TAG_CREATE_PROFILE_ACTIVITY_FRAGMENT
) as? CreateProfileFragment
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.oppia.android.app.onboarding

import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableFragment
import javax.inject.Inject

/** Fragment for displaying a new learner profile creation flow. */
class CreateProfileFragment : InjectableFragment() {
@Inject
lateinit var createProfileFragmentPresenter: CreateProfileFragmentPresenter

override fun onAttach(context: Context) {
super.onAttach(context)
(fragmentComponent as FragmentComponentImpl).inject(this)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
createProfileFragmentPresenter.activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
createProfileFragmentPresenter.handleOnActivityResult(result.data)
}
}
return createProfileFragmentPresenter.handleCreateView(inflater, container)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.oppia.android.app.onboarding

import android.content.Intent
import android.graphics.PorterDuff
import android.provider.MediaStore
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import org.oppia.android.R
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.databinding.CreateProfileFragmentBinding
import org.oppia.android.util.parser.image.ImageLoader
import org.oppia.android.util.parser.image.ImageViewTarget
import javax.inject.Inject

/** Presenter for [CreateProfileFragment]. */
@FragmentScope
class CreateProfileFragmentPresenter @Inject constructor(
private val fragment: Fragment,
private val activity: AppCompatActivity,
private val createProfileViewModel: CreateProfileViewModel,
private val imageLoader: ImageLoader
) {
private lateinit var binding: CreateProfileFragmentBinding
private lateinit var uploadImageView: ImageView
private lateinit var selectedImage: String

/** Launcher for picking an image from device gallery. */
lateinit var activityResultLauncher: ActivityResultLauncher<Intent>

/** Initialize layout bindings. */
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View {
binding = CreateProfileFragmentBinding.inflate(
inflater,
container,
/* attachToRoot= */ false
)
binding.let {
it.lifecycleOwner = fragment
it.viewModel = createProfileViewModel
}

uploadImageView = binding.createProfileUserImageView

uploadImageView.apply {
setColorFilter(
ResourcesCompat.getColor(
activity.resources,
R.color.component_color_avatar_background_25_color,
null
),
PorterDuff.Mode.DST_OVER
)

imageLoader.loadDrawable(
R.drawable.ic_profile_icon,
ImageViewTarget(this)
)
}

binding.onboardingNavigationContinue.setOnClickListener {
val nickname = binding.createProfileNicknameEdittext.text.toString().trim()

createProfileViewModel.hasErrorMessage.set(nickname.isBlank())

if (createProfileViewModel.hasErrorMessage.get() != true) {
val intent = IntroActivity.createIntroActivity(activity, nickname)
fragment.startActivity(intent)
}
}

binding.createProfileNicknameEdittext.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
createProfileViewModel.hasErrorMessage.set(false)
}
})

addViewOnClickListeners(binding)

return binding.root
}

/** Receive the result of image upload and load it into the image view. */
fun handleOnActivityResult(intent: Intent?) {
intent?.let {
binding.createProfilePicturePrompt.visibility = View.GONE
selectedImage =
checkNotNull(intent.data.toString()) { "Could not find the selected image." }
imageLoader.loadBitmap(
selectedImage,
ImageViewTarget(uploadImageView)
)
}
}

private fun addViewOnClickListeners(binding: CreateProfileFragmentBinding) {
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

binding.onboardingNavigationBack.setOnClickListener { activity.finish() }
binding.createProfileEditPictureIcon.setOnClickListener {
activityResultLauncher.launch(
galleryIntent
)
}
binding.createProfilePicturePrompt.setOnClickListener {
activityResultLauncher.launch(
galleryIntent
)
}
binding.createProfileUserImageView.setOnClickListener {
activityResultLauncher.launch(
galleryIntent
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.oppia.android.app.onboarding

import androidx.databinding.ObservableField
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.viewmodel.ObservableViewModel
import javax.inject.Inject

/** The ViewModel for [CreateProfileFragment]. */
@FragmentScope
class CreateProfileViewModel @Inject constructor() : ObservableViewModel() {

/** ObservableField that tracks whether creating a nickname has triggered an error condition. */
val hasErrorMessage = ObservableField(false)
}
Loading

0 comments on commit 3c5236b

Please sign in to comment.