From 500eb113b648e3ad84b26e6d521cf9731af1fa50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 Nov 2019 20:36:29 +0100 Subject: [PATCH] Login screens: add some animation for Fragment transition (WIP) --- .../features/login/AbstractLoginFragment.kt | 10 ++ .../riotx/features/login/LoginActivity.kt | 92 ++++++++++++++----- .../login/LoginServerSelectionFragment.kt | 10 -- vector/src/main/res/anim/enter_fade_in.xml | 4 +- vector/src/main/res/anim/exit_fade_out.xml | 5 +- .../fragment_login_server_selection.xml | 7 +- .../main/res/layout/fragment_login_splash.xml | 3 + .../src/main/res/values-v21/styles_login.xml | 8 ++ vector/src/main/res/values/integers.xml | 6 +- vector/src/main/res/values/styles_login.xml | 4 +- 10 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 vector/src/main/res/values-v21/styles_login.xml diff --git a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt index fa66de32da..6cca32cf7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt @@ -16,10 +16,12 @@ package im.vector.riotx.features.login +import android.os.Build import android.os.Bundle import android.view.View import androidx.annotation.CallSuper import androidx.appcompat.app.AlertDialog +import androidx.transition.TransitionInflater import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.failure.Failure @@ -42,6 +44,14 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed { // Due to async, we keep a boolean to avoid displaying twice the cancellation dialog private var displayCancelDialog = true + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) + } + } + @CallSuper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt index 8180fa216f..eb1dc4a0e6 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt @@ -19,11 +19,15 @@ package im.vector.riotx.features.login import android.content.Context import android.content.Intent import android.view.View +import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar import androidx.core.view.ViewCompat +import androidx.core.view.children import androidx.core.view.isVisible +import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.auth.registration.FlowResult @@ -56,6 +60,27 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { injector.inject(this) } + private val enterAnim = R.anim.enter_fade_in + private val exitAnim = R.anim.exit_fade_out + + private val popEnterAnim = R.anim.no_anim + private val popExitAnim = R.anim.exit_fade_out + + private val topFragment: Fragment? + get() = supportFragmentManager.findFragmentById(R.id.loginFragmentContainer) + + private val commonOption: (FragmentTransaction) -> Unit = { ft -> + // Find the loginLogo on the current Fragment, this should not return null + (topFragment?.view as? ViewGroup) + // Find findViewById does not work, I do not know why + // findViewById(R.id.loginLogo) + ?.children + ?.first { it.id == R.id.loginLogo } + ?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // TODO + ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) + } + override fun getLayoutRes() = R.layout.activity_login override fun initUiAndData() { @@ -96,31 +121,37 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { @Suppress("UNUSED_VARIABLE") val dummy = when (loginNavigation) { is LoginNavigation.OpenServerSelection -> - addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java, + addFragmentToBackstack(R.id.loginFragmentContainer, + LoginServerSelectionFragment::class.java, option = { ft -> - val view = findViewById(R.id.loginSplashLogo) - if (view != null) { - ft.addSharedElement(view, ViewCompat.getTransitionName(view) ?: "") - } + findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // TODO Disabled because it provokes a flickering + //ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) }) is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone() is LoginNavigation.OnSignModeSelected -> onSignModeSelected() is LoginNavigation.OnLoginFlowRetrieved -> addFragmentToBackstack(R.id.loginFragmentContainer, - LoginSignUpSignInSelectionFragment::class.java) + LoginSignUpSignInSelectionFragment::class.java, + option = commonOption) is LoginNavigation.OnWebLoginError -> onWebLoginError(loginNavigation) is LoginNavigation.OnForgetPasswordClicked -> addFragmentToBackstack(R.id.loginFragmentContainer, - LoginResetPasswordFragment::class.java) + LoginResetPasswordFragment::class.java, + option = commonOption) is LoginNavigation.OnResetPasswordSendThreePidDone -> { supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE) addFragmentToBackstack(R.id.loginFragmentContainer, - LoginResetPasswordMailConfirmationFragment::class.java) + LoginResetPasswordMailConfirmationFragment::class.java, + option = commonOption) } is LoginNavigation.OnResetPasswordMailConfirmationSuccess -> { supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE) addFragmentToBackstack(R.id.loginFragmentContainer, - LoginResetPasswordSuccessFragment::class.java) + LoginResetPasswordSuccessFragment::class.java, + option = commonOption) } is LoginNavigation.OnResetPasswordMailConfirmationSuccessDone -> { // Go back to the login fragment @@ -130,12 +161,14 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { addFragmentToBackstack(R.id.loginFragmentContainer, LoginWaitForEmailFragment::class.java, LoginWaitForEmailFragmentArgument(loginNavigation.email), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) is LoginNavigation.OnSendMsisdnSuccess -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginGenericTextInputFormFragment::class.java, LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginNavigation.msisdn), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) } } @@ -156,7 +189,9 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { // This way it will be automatically popped in when starting the next registration stage addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java, - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption + ) } } } @@ -200,7 +235,9 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { when (state.serverType) { ServerType.MatrixOrg -> Unit // In this case, we wait for the login flow ServerType.Modular, - ServerType.Other -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerUrlFormFragment::class.java) + ServerType.Other -> addFragmentToBackstack(R.id.loginFragmentContainer, + LoginServerUrlFormFragment::class.java, + option = commonOption) } } @@ -214,8 +251,13 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { // It depends on the LoginMode when (state.loginMode) { LoginMode.Unknown -> error("Developer error") - LoginMode.Password -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java, tag = FRAGMENT_LOGIN_TAG) - LoginMode.Sso -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginWebFragment::class.java) + LoginMode.Password -> addFragmentToBackstack(R.id.loginFragmentContainer, + LoginFragment::class.java, + tag = FRAGMENT_LOGIN_TAG, + option = commonOption) + LoginMode.Sso -> addFragmentToBackstack(R.id.loginFragmentContainer, + LoginWebFragment::class.java, + option = commonOption) LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes) } } @@ -227,7 +269,9 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { .setTitle(R.string.app_name) .setMessage(getString(R.string.login_registration_not_supported)) .setPositiveButton(R.string.yes) { _, _ -> - addFragmentToBackstack(R.id.loginFragmentContainer, LoginWebFragment::class.java) + addFragmentToBackstack(R.id.loginFragmentContainer, + LoginWebFragment::class.java, + option = commonOption) } .setNegativeButton(R.string.no, null) .show() @@ -238,7 +282,9 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { .setTitle(R.string.app_name) .setMessage(getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" })) .setPositiveButton(R.string.yes) { _, _ -> - addFragmentToBackstack(R.id.loginFragmentContainer, LoginWebFragment::class.java) + addFragmentToBackstack(R.id.loginFragmentContainer, + LoginWebFragment::class.java, + option = commonOption) } .setNegativeButton(R.string.no, null) .show() @@ -269,19 +315,23 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { is Stage.ReCaptcha -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginCaptchaFragment::class.java, LoginCaptchaFragmentArgument(stage.publicKey), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) is Stage.Email -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginGenericTextInputFormFragment::class.java, LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) is Stage.Msisdn -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginGenericTextInputFormFragment::class.java, LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) is Stage.Terms -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginTermsFragment::class.java, LoginTermsFragmentArgument(stage.policies.toLocalizedLoginTerms(getString(R.string.resources_language))), - tag = FRAGMENT_REGISTRATION_STAGE_TAG) + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) else -> Unit // Should not happen } } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginServerSelectionFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginServerSelectionFragment.kt index 8cd097318f..6e427d0bdb 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginServerSelectionFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginServerSelectionFragment.kt @@ -16,11 +16,9 @@ package im.vector.riotx.features.login -import android.os.Build import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.transition.TransitionInflater import butterknife.OnClick import com.airbnb.mvrx.withState import im.vector.riotx.R @@ -39,14 +37,6 @@ class LoginServerSelectionFragment @Inject constructor( override fun getLayoutResId() = R.layout.fragment_login_server_selection - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/vector/src/main/res/anim/enter_fade_in.xml b/vector/src/main/res/anim/enter_fade_in.xml index 292e35edde..8326050fdc 100644 --- a/vector/src/main/res/anim/enter_fade_in.xml +++ b/vector/src/main/res/anim/enter_fade_in.xml @@ -1,10 +1,10 @@ + android:startOffset="@integer/default_animation_offset"> - \ No newline at end of file + diff --git a/vector/src/main/res/anim/exit_fade_out.xml b/vector/src/main/res/anim/exit_fade_out.xml index 28934ead10..b24bb6724c 100644 --- a/vector/src/main/res/anim/exit_fade_out.xml +++ b/vector/src/main/res/anim/exit_fade_out.xml @@ -1,10 +1,9 @@ + android:duration="@integer/default_animation_half"> - \ No newline at end of file + diff --git a/vector/src/main/res/layout/fragment_login_server_selection.xml b/vector/src/main/res/layout/fragment_login_server_selection.xml index a2b5245ac3..c97b32bd21 100644 --- a/vector/src/main/res/layout/fragment_login_server_selection.xml +++ b/vector/src/main/res/layout/fragment_login_server_selection.xml @@ -9,7 +9,6 @@ @@ -25,6 +24,8 @@ android:layout_height="wrap_content" android:text="@string/login_server_title" android:textAppearance="@style/TextAppearance.Vector.Login.Title" + android:transitionName="loginTitleTransition" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -148,7 +149,7 @@ @@ -183,6 +183,7 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="@string/login_continue" + android:transitionName="loginSubmitTransition" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/vector/src/main/res/layout/fragment_login_splash.xml b/vector/src/main/res/layout/fragment_login_splash.xml index 593ac26276..44a81df539 100644 --- a/vector/src/main/res/layout/fragment_login_splash.xml +++ b/vector/src/main/res/layout/fragment_login_splash.xml @@ -37,7 +37,9 @@ android:layout_marginTop="48dp" android:text="@string/login_splash_title" android:textAppearance="@style/TextAppearance.Vector.Login.Title" + android:transitionName="loginTitleTransition" app:layout_constraintBottom_toTopOf="@+id/loginSplashText1" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginSplashLogo" /> @@ -123,6 +125,7 @@ android:layout_height="wrap_content" android:layout_marginTop="48dp" android:text="@string/login_splash_submit" + android:transitionName="loginSubmitTransition" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/vector/src/main/res/values-v21/styles_login.xml b/vector/src/main/res/values-v21/styles_login.xml new file mode 100644 index 0000000000..22eeec5528 --- /dev/null +++ b/vector/src/main/res/values-v21/styles_login.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/integers.xml b/vector/src/main/res/values/integers.xml index 59c1327f30..75e8bb6f9a 100644 --- a/vector/src/main/res/values/integers.xml +++ b/vector/src/main/res/values/integers.xml @@ -1,7 +1,11 @@ - 500 + 200 + + 400 + + 200 0 diff --git a/vector/src/main/res/values/styles_login.xml b/vector/src/main/res/values/styles_login.xml index c753e13aff..3bcda048dc 100644 --- a/vector/src/main/res/values/styles_login.xml +++ b/vector/src/main/res/values/styles_login.xml @@ -22,7 +22,9 @@ parent -