diff --git a/app/BUILD.bazel b/app/BUILD.bazel index 7ddcebe5300..26ee5509faf 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -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", diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b25b8ccfc86..0c545bf411e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -340,7 +340,10 @@ android:name=".app.onboarding.OnboardingProfileTypeActivity" android:label="@string/onboarding_profile_type_activity_title" android:theme="@style/OppiaThemeWithoutActionBar" /> - + + if (result.resultCode == Activity.RESULT_OK) { + createProfileFragmentPresenter.handleOnActivityResult(result.data) + } + } + return createProfileFragmentPresenter.handleCreateView(inflater, container) + } +} diff --git a/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileFragmentPresenter.kt new file mode 100644 index 00000000000..d3a57c61988 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileFragmentPresenter.kt @@ -0,0 +1,120 @@ +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 + + /** 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()) + } + + 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 + ) + } + } +} diff --git a/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileViewModel.kt b/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileViewModel.kt new file mode 100644 index 00000000000..e6ef763f23c --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/onboarding/CreateProfileViewModel.kt @@ -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) +} diff --git a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt index 893960b55c7..72ae543dd0c 100644 --- a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt @@ -27,6 +27,11 @@ class OnboardingProfileTypeFragmentPresenter @Inject constructor( binding.apply { lifecycleOwner = fragment + profileTypeLearnerNavigationCard.setOnClickListener { + val intent = CreateProfileActivity.createProfileActivityIntent(activity) + fragment.startActivity(intent) + } + profileTypeSupervisorNavigationCard.setOnClickListener { val intent = ProfileChooserActivity.createProfileChooserActivity(activity) fragment.startActivity(intent) @@ -36,6 +41,7 @@ class OnboardingProfileTypeFragmentPresenter @Inject constructor( activity.finish() } } + return binding.root } } diff --git a/app/src/main/res/drawable/create_profile_picture_icon.xml b/app/src/main/res/drawable/create_profile_picture_icon.xml new file mode 100644 index 00000000000..0aa1cd192f0 --- /dev/null +++ b/app/src/main/res/drawable/create_profile_picture_icon.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/edit_text_white_background_error_border.xml b/app/src/main/res/drawable/edit_text_white_background_error_border.xml new file mode 100644 index 00000000000..2851e1fc4cb --- /dev/null +++ b/app/src/main/res/drawable/edit_text_white_background_error_border.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/edit_text_white_background_with_border.xml b/app/src/main/res/drawable/edit_text_white_background_with_border.xml new file mode 100644 index 00000000000..90e111c7c1a --- /dev/null +++ b/app/src/main/res/drawable/edit_text_white_background_with_border.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_outline_edit_24.xml b/app/src/main/res/drawable/ic_outline_edit_24.xml new file mode 100644 index 00000000000..407f3e2f737 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_edit_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_profile_icon.xml b/app/src/main/res/drawable/ic_profile_icon.xml new file mode 100644 index 00000000000..7b7c22f999b --- /dev/null +++ b/app/src/main/res/drawable/ic_profile_icon.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-land/create_profile_fragment.xml b/app/src/main/res/layout-land/create_profile_fragment.xml new file mode 100644 index 00000000000..93c01c2f32d --- /dev/null +++ b/app/src/main/res/layout-land/create_profile_fragment.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +