Login screens: add some animation for Fragment transition (WIP)

This commit is contained in:
Benoit Marty 2019-11-28 20:36:29 +01:00
parent 1bec8c29b8
commit 500eb113b6
10 changed files with 108 additions and 41 deletions

View file

@ -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)

View file

@ -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<View?>(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<View?>(R.id.loginSplashLogo)
if (view != null) {
ft.addSharedElement(view, ViewCompat.getTransitionName(view) ?: "")
}
findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(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
}
}

View file

@ -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)

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/default_animation_duration"
android:shareInterpolator="false">
android:startOffset="@integer/default_animation_offset">
<alpha
android:fromAlpha="0"
android:toAlpha="1" />
</set>
</set>

View file

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/default_animation_duration"
android:shareInterpolator="false">
android:duration="@integer/default_animation_half">
<alpha
android:fromAlpha="1"
android:toAlpha="0" />
</set>
</set>

View file

@ -9,7 +9,6 @@
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
android:transitionName="loginLogoTransition"
tools:ignore="ContentDescription,MissingConstraints,UnusedAttribute" />
<!-- Missing attributes are in the style -->
@ -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 @@
<TextView
android:id="@+id/loginServerChoiceOtherTitle"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:text="@string/login_server_other_title"
@ -156,7 +157,6 @@
android:textColor="?riotx_text_primary"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/loginServerChoiceOtherText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
@ -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"

View file

@ -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"

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LoginLogo.v21" parent="LoginLogoBase">
<item name="android:transitionName">loginLogoTransition</item>
</style>
</resources>

View file

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="default_animation_duration">500</integer>
<integer name="default_animation_half">200</integer>
<integer name="default_animation_duration">400</integer>
<integer name="default_animation_offset">200</integer>
<integer name="rtl_mirror_flip">0</integer>

View file

@ -22,7 +22,9 @@
<item name="layout_constraintStart_toStartOf">parent</item>
</style>
<style name="LoginLogo" parent="LoginLogoBase">
<style name="LoginLogo.v21" parent="LoginLogoBase" />
<style name="LoginLogo" parent="LoginLogo.v21">
<item name="layout_constraintEnd_toEndOf">parent</item>
<item name="android:layout_marginTop">32dp</item>
</style>