From 9a02543afd270451f945e57e495cc3d87de9718e Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 7 Mar 2022 14:07:22 +0000 Subject: [PATCH] FTUE - Choose a display picture (#5323) * adding tests around the onboarding view model - cases for the personalisation and display name actions * adding base choose name fragment with UI * add click handling for the display name actions * adding tests around the onboarding view model - cases for the personalisation and display name actions * adding barebones profile picture fragment with ability to select a user avatar * extracting uri filename resolving to a class which can be injected - includes tests * updating upstream avatar on profile picture save and continue step - moves the personalisation state to a dedicated model to allow for back and forth state restoration * adding test case for skipping profile picture setting * taking the profile loading into account when rendering the onboarding loading * extracting method for the handling of the profile picture selection * adding dedicated camera icon for choosing profile picture * adding toolbar to back to profile picture page - this toolbar will fade in with the fragment as it sits at the fragment level, probably worth revisiting once more pages have a toolbar * changing edit/add picture icon based on if we're already selected an image * making use of debounced clicks to avoid potential extra clicks * making the avatar height and camera icon relative percentage based - also makes the avatar itself clicking, including a foreground ripple * fixing formatting * making use of fake session id for user id assertion * using a real matrix id syntax for the fake session user id * removing duplicated dimens * using self closing imageview tag --- .../lib/multipicker/utils/CursorExtensions.kt | 5 + .../ui-styles/src/main/res/values/dimens.xml | 3 + .../im/vector/app/core/di/FragmentModule.kt | 6 + .../app/features/home/AvatarRenderer.kt | 12 ++ .../features/onboarding/OnboardingAction.kt | 4 + .../onboarding/OnboardingViewEvents.kt | 2 + .../onboarding/OnboardingViewModel.kt | 52 +++++- .../onboarding/OnboardingViewState.kt | 16 +- .../onboarding/UriFilenameResolver.kt | 41 +++++ .../FtueAuthChooseDisplayNameFragment.kt | 12 +- .../FtueAuthChooseProfilePictureFragment.kt | 96 ++++++++++ .../onboarding/ftueauth/FtueAuthVariant.kt | 9 +- .../layout/fragment_ftue_profile_picture.xml | 164 ++++++++++++++++++ vector/src/main/res/values/donottranslate.xml | 4 + .../quads/SharedSecureStorageViewModelTest.kt | 10 +- .../onboarding/OnboardingViewModelTest.kt | 121 +++++++++++-- .../onboarding/UriFilenameResolverTest.kt | 85 +++++++++ .../java/im/vector/app/test/Extensions.kt | 17 +- .../im/vector/app/test/FlowTestObserver.kt | 13 +- .../app/test/fakes/FakeContentResolver.kt | 32 ++++ .../im/vector/app/test/fakes/FakeContext.kt | 5 +- .../im/vector/app/test/fakes/FakeCursor.kt | 43 +++++ .../app/test/fakes/FakeProfileService.kt | 21 ++- .../im/vector/app/test/fakes/FakeSession.kt | 4 + .../java/im/vector/app/test/fakes/FakeUri.kt | 34 ++++ .../app/test/fakes/FakeUriFilenameResolver.kt | 31 ++++ 26 files changed, 803 insertions(+), 39 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/UriFilenameResolver.kt create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt create mode 100644 vector/src/main/res/layout/fragment_ftue_profile_picture.xml create mode 100644 vector/src/test/java/im/vector/app/features/onboarding/UriFilenameResolverTest.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeContentResolver.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeCursor.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeUri.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeUriFilenameResolver.kt diff --git a/library/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt b/library/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt index 87cf48d0a75..72880babbf5 100644 --- a/library/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt +++ b/library/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt @@ -17,7 +17,12 @@ package im.vector.lib.multipicker.utils import android.database.Cursor +import androidx.core.database.getStringOrNull fun Cursor.getColumnIndexOrNull(column: String): Int? { return getColumnIndex(column).takeIf { it != -1 } } + +fun Cursor.readStringColumnOrNull(column: String): String? { + return getColumnIndexOrNull(column)?.let { getStringOrNull(it) } +} diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index be57f75dc87..db42cfa12ce 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -67,4 +67,7 @@ 0.01 0.35 + + 0.15 + 0.05 \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index c62f8379477..2ffdd7ddf3b 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -100,6 +100,7 @@ import im.vector.app.features.matrixto.MatrixToUserFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment +import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment @@ -485,6 +486,11 @@ interface FragmentModule { @FragmentKey(FtueAuthChooseDisplayNameFragment::class) fun bindFtueAuthChooseDisplayNameFragment(fragment: FtueAuthChooseDisplayNameFragment): Fragment + @Binds + @IntoMap + @FragmentKey(FtueAuthChooseProfilePictureFragment::class) + fun bindFtueAuthChooseProfilePictureFragment(fragment: FtueAuthChooseProfilePictureFragment): Fragment + @Binds @IntoMap @FragmentKey(UserListFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt index 3678808b2d2..326b350f306 100644 --- a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.net.Uri import android.widget.ImageView import androidx.annotation.AnyThread import androidx.annotation.ColorInt @@ -48,6 +49,7 @@ import org.matrix.android.sdk.api.auth.login.LoginProfileInfo import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.util.MatrixItem +import java.io.File import javax.inject.Inject /** @@ -100,6 +102,16 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active DrawableImageViewTarget(imageView)) } + @UiThread + fun render(matrixItem: MatrixItem, localUri: Uri?, imageView: ImageView) { + val placeholder = getPlaceholderDrawable(matrixItem) + GlideApp.with(imageView) + .load(localUri?.let { File(localUri.path!!) }) + .apply(RequestOptions.circleCropTransform()) + .placeholder(placeholder) + .into(imageView) + } + @UiThread fun render(mappedContact: MappedContact, imageView: ImageView) { // Create a Fake MatrixItem, for the placeholder diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt index b8fd2255f05..b35c110892d 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt @@ -16,6 +16,7 @@ package im.vector.app.features.onboarding +import android.net.Uri import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.ServerType @@ -76,4 +77,7 @@ sealed class OnboardingAction : VectorViewModelAction { data class UpdateDisplayName(val displayName: String) : OnboardingAction() object UpdateDisplayNameSkipped : OnboardingAction() + data class ProfilePictureSelected(val uri: Uri) : OnboardingAction() + object SaveSelectedProfilePicture : OnboardingAction() + object UpdateProfilePictureSkipped : OnboardingAction() } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt index 159efd1b132..8a09879b15a 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt @@ -54,4 +54,6 @@ sealed class OnboardingViewEvents : VectorViewEvents { object OnPersonalizeProfile : OnboardingViewEvents() object OnDisplayNameUpdated : OnboardingViewEvents() object OnDisplayNameSkipped : OnboardingViewEvents() + object OnPersonalizationComplete : OnboardingViewEvents() + object OnBack : OnboardingViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 1fadea05043..413745f98c8 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -64,6 +64,7 @@ import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixIdFailure import org.matrix.android.sdk.api.session.Session import timber.log.Timber +import java.util.UUID import java.util.concurrent.CancellationException /** @@ -80,6 +81,7 @@ class OnboardingViewModel @AssistedInject constructor( private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, private val analyticsTracker: AnalyticsTracker, + private val uriFilenameResolver: UriFilenameResolver, private val vectorOverrides: VectorOverrides ) : VectorViewModel(initialState) { @@ -157,6 +159,9 @@ class OnboardingViewModel @AssistedInject constructor( is OnboardingAction.PostViewEvent -> _viewEvents.post(action.viewEvent) is OnboardingAction.UpdateDisplayName -> updateDisplayName(action.displayName) OnboardingAction.UpdateDisplayNameSkipped -> _viewEvents.post(OnboardingViewEvents.OnDisplayNameSkipped) + OnboardingAction.UpdateProfilePictureSkipped -> _viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete) + is OnboardingAction.ProfilePictureSelected -> handleProfilePictureSelected(action) + OnboardingAction.SaveSelectedProfilePicture -> updateProfilePicture() }.exhaustive } @@ -899,7 +904,12 @@ class OnboardingViewModel @AssistedInject constructor( val activeSession = activeSessionHolder.getActiveSession() try { activeSession.setDisplayName(activeSession.myUserId, displayName) - setState { copy(asyncDisplayName = Success(Unit)) } + setState { + copy( + asyncDisplayName = Success(Unit), + personalizationState = personalizationState.copy(displayName = displayName) + ) + } _viewEvents.post(OnboardingViewEvents.OnDisplayNameUpdated) } catch (error: Throwable) { setState { copy(asyncDisplayName = Fail(error)) } @@ -907,6 +917,46 @@ class OnboardingViewModel @AssistedInject constructor( } } } + + private fun handleProfilePictureSelected(action: OnboardingAction.ProfilePictureSelected) { + setState { + copy(personalizationState = personalizationState.copy(selectedPictureUri = action.uri)) + } + } + + private fun updateProfilePicture() { + withState { state -> + when (val pictureUri = state.personalizationState.selectedPictureUri) { + null -> _viewEvents.post(OnboardingViewEvents.Failure(NullPointerException("picture uri is missing from state"))) + else -> { + setState { copy(asyncProfilePicture = Loading()) } + viewModelScope.launch { + val activeSession = activeSessionHolder.getActiveSession() + try { + activeSession.updateAvatar( + activeSession.myUserId, + pictureUri, + uriFilenameResolver.getFilenameFromUri(pictureUri) ?: UUID.randomUUID().toString() + ) + setState { + copy( + asyncProfilePicture = Success(Unit), + ) + } + onProfilePictureSaved() + } catch (error: Throwable) { + setState { copy(asyncProfilePicture = Fail(error)) } + _viewEvents.post(OnboardingViewEvents.Failure(error)) + } + } + } + } + } + } + + private fun onProfilePictureSaved() { + _viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete) + } } private fun LoginMode.supportsSignModeScreen(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt index 61674de8ae0..bd5d93ae4d1 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt @@ -16,6 +16,8 @@ package im.vector.app.features.onboarding +import android.net.Uri +import android.os.Parcelable import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState @@ -25,6 +27,7 @@ import com.airbnb.mvrx.Uninitialized import im.vector.app.features.login.LoginMode import im.vector.app.features.login.ServerType import im.vector.app.features.login.SignMode +import kotlinx.parcelize.Parcelize data class OnboardingViewState( val asyncLoginAction: Async = Uninitialized, @@ -33,6 +36,7 @@ data class OnboardingViewState( val asyncResetMailConfirmed: Async = Uninitialized, val asyncRegistration: Async = Uninitialized, val asyncDisplayName: Async = Uninitialized, + val asyncProfilePicture: Async = Uninitialized, @PersistState val onboardingFlow: OnboardingFlow? = null, @@ -65,6 +69,9 @@ data class OnboardingViewState( val loginModeSupportedTypes: List = emptyList(), val knownCustomHomeServersUrls: List = emptyList(), val isForceLoginFallbackEnabled: Boolean = false, + + @PersistState + val personalizationState: PersonalizationState = PersonalizationState() ) : MavericksState { fun isLoading(): Boolean { @@ -73,7 +80,8 @@ data class OnboardingViewState( asyncResetPassword is Loading || asyncResetMailConfirmed is Loading || asyncRegistration is Loading || - asyncDisplayName is Loading + asyncDisplayName is Loading || + asyncProfilePicture is Loading } fun isAuthTaskCompleted(): Boolean { @@ -86,3 +94,9 @@ enum class OnboardingFlow { SignUp, SignInSignUp } + +@Parcelize +data class PersonalizationState( + val displayName: String? = null, + val selectedPictureUri: Uri? = null +) : Parcelable diff --git a/vector/src/main/java/im/vector/app/features/onboarding/UriFilenameResolver.kt b/vector/src/main/java/im/vector/app/features/onboarding/UriFilenameResolver.kt new file mode 100644 index 00000000000..7c622663db7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/UriFilenameResolver.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.onboarding + +import android.content.Context +import android.net.Uri +import android.provider.OpenableColumns +import im.vector.lib.multipicker.utils.readStringColumnOrNull +import javax.inject.Inject + +class UriFilenameResolver @Inject constructor(private val context: Context) { + + fun getFilenameFromUri(uri: Uri): String? { + val fallback = uri.path?.substringAfterLast('/') + return when (uri.scheme) { + "content" -> readResolvedDisplayName(uri) ?: fallback + else -> fallback + } + } + + private fun readResolvedDisplayName(uri: Uri): String? { + return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + cursor.takeIf { cursor.moveToFirst() } + ?.readStringColumnOrNull(OpenableColumns.DISPLAY_NAME) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseDisplayNameFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseDisplayNameFragment.kt index f1bf3d4a86d..1ce0c544e5b 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseDisplayNameFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseDisplayNameFragment.kt @@ -27,6 +27,7 @@ import im.vector.app.core.platform.SimpleTextWatcher import im.vector.app.databinding.FragmentFtueDisplayNameBinding import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingViewEvents +import im.vector.app.features.onboarding.OnboardingViewState import javax.inject.Inject class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuthFragment() { @@ -41,7 +42,6 @@ class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuth } private fun setupViews() { - views.displayNameSubmit.isEnabled = views.displayNameInput.hasContentEmpty() views.displayNameInput.editText?.addTextChangedListener(object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { val newContent = s.toString() @@ -58,10 +58,7 @@ class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuth } } - views.displayNameSubmit.debouncedClicks { - updateDisplayName() - } - + views.displayNameSubmit.debouncedClicks { updateDisplayName() } views.displayNameSkip.debouncedClicks { viewModel.handle(OnboardingAction.UpdateDisplayNameSkipped) } } @@ -70,6 +67,11 @@ class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuth viewModel.handle(OnboardingAction.UpdateDisplayName(newDisplayName)) } + override fun updateWithState(state: OnboardingViewState) { + views.displayNameInput.editText?.setText(state.personalizationState.displayName) + views.displayNameSubmit.isEnabled = views.displayNameInput.hasContentEmpty() + } + override fun resetViewModel() { // Nothing to do } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt new file mode 100644 index 00000000000..bc1bf0c8bce --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.onboarding.ftueauth + +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper +import im.vector.app.core.extensions.singletonEntryPoint +import im.vector.app.core.resources.ColorProvider +import im.vector.app.databinding.FragmentFtueProfilePictureBinding +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.onboarding.OnboardingAction +import im.vector.app.features.onboarding.OnboardingViewEvents +import im.vector.app.features.onboarding.OnboardingViewState +import org.matrix.android.sdk.api.util.MatrixItem +import javax.inject.Inject + +class FtueAuthChooseProfilePictureFragment @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + colorProvider: ColorProvider +) : AbstractFtueAuthFragment(), GalleryOrCameraDialogHelper.Listener { + + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val avatarRenderer: AvatarRenderer by lazy { requireContext().singletonEntryPoint().avatarRenderer() } + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueProfilePictureBinding { + return FragmentFtueProfilePictureBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupViews() + } + + private fun setupViews() { + views.profilePictureToolbar.setNavigationOnClickListener { + viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnBack)) + } + views.changeProfilePictureButton.debouncedClicks { galleryOrCameraDialogHelper.show() } + views.profilePictureView.debouncedClicks { galleryOrCameraDialogHelper.show() } + + views.profilePictureSubmit.debouncedClicks { + withState(viewModel) { + viewModel.handle(OnboardingAction.SaveSelectedProfilePicture) + } + } + + views.profilePictureSkip.debouncedClicks { viewModel.handle(OnboardingAction.UpdateProfilePictureSkipped) } + } + + override fun updateWithState(state: OnboardingViewState) { + val hasSetPicture = state.personalizationState.selectedPictureUri != null + views.profilePictureSubmit.isEnabled = hasSetPicture + views.changeProfilePictureIcon.setImageResource(if (hasSetPicture) R.drawable.ic_edit else R.drawable.ic_camera_plain) + + val session = activeSessionHolder.getActiveSession() + val matrixItem = MatrixItem.UserItem( + id = session.myUserId, + displayName = state.personalizationState.displayName ?: "" + ) + avatarRenderer.render(matrixItem, localUri = state.personalizationState.selectedPictureUri, imageView = views.profilePictureView) + } + + override fun onImageReady(uri: Uri?) { + if (uri == null) { + Toast.makeText(requireContext(), "Cannot retrieve cropped value", Toast.LENGTH_SHORT).show() + } else { + viewModel.handle(OnboardingAction.ProfilePictureSelected(uri)) + } + } + + override fun resetViewModel() { + // Nothing to do + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index 4259e90cbe4..e336419e3f0 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -32,6 +32,7 @@ import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.popBackstack import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.ScreenOrientationLocker import im.vector.app.core.platform.VectorBaseActivity @@ -235,6 +236,8 @@ class FtueAuthVariant( OnboardingViewEvents.OnTakeMeHome -> navigateToHome(createdAccount = true) OnboardingViewEvents.OnDisplayNameUpdated -> onDisplayNameUpdated() OnboardingViewEvents.OnDisplayNameSkipped -> onDisplayNameUpdated() + OnboardingViewEvents.OnPersonalizationComplete -> navigateToHome(createdAccount = true) + OnboardingViewEvents.OnBack -> activity.popBackstack() }.exhaustive } @@ -421,7 +424,9 @@ class FtueAuthVariant( } private fun onDisplayNameUpdated() { - // TODO go to the real profile picture fragment - navigateToHome(createdAccount = true) + activity.addFragmentToBackstack(views.loginFragmentContainer, + FtueAuthChooseProfilePictureFragment::class.java, + option = commonOption + ) } } diff --git a/vector/src/main/res/layout/fragment_ftue_profile_picture.xml b/vector/src/main/res/layout/fragment_ftue_profile_picture.xml new file mode 100644 index 00000000000..0def0880628 --- /dev/null +++ b/vector/src/main/res/layout/fragment_ftue_profile_picture.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +