From 89d6a136118915140b11adddde6da138a330d304 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 9 Jan 2024 19:34:09 +0900 Subject: [PATCH] Login fields should support password managers. Closes #99 Upping versionCode to 39 --- .../androidx/content/ViewExtensions.kt | 44 +++++++ .../android/screens/login/LoginActivity.kt | 68 ++++++++-- .../android/screens/login/LoginViewModel.kt | 2 +- .../drawable/round_corners_blue_button.xml | 6 + app/src/main/res/layout/login_activity.xml | 123 ++++++++++++++++++ app/src/main/res/values-night/colors.xml | 2 + app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/styles.xml | 20 +++ buildSrc/src/main/kotlin/BuildConfig.kt | 2 +- 9 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/org/zotero/android/androidx/content/ViewExtensions.kt create mode 100644 app/src/main/res/drawable/round_corners_blue_button.xml create mode 100644 app/src/main/res/layout/login_activity.xml diff --git a/app/src/main/java/org/zotero/android/androidx/content/ViewExtensions.kt b/app/src/main/java/org/zotero/android/androidx/content/ViewExtensions.kt new file mode 100644 index 00000000..d0a01b53 --- /dev/null +++ b/app/src/main/java/org/zotero/android/androidx/content/ViewExtensions.kt @@ -0,0 +1,44 @@ +package org.zotero.android.androidx.content + +import android.graphics.Color.WHITE +import android.view.View +import androidx.annotation.StringRes +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import com.google.android.material.snackbar.Snackbar + +fun View.longErrorSnackbar( + @StringRes text: Int, + @StringRes actionText: Int? = null, + action: (() -> Unit)? = null, +) { + val snackbar = Snackbar + .make(this, text, Snackbar.LENGTH_LONG) + + if (action != null && actionText != null) { + snackbar.setAction(actionText) { action.invoke() } + } + + snackbar.show() +} + +fun View.longErrorSnackbar( + text: String, + duration: Int? = null, + @StringRes actionText: Int? = null, + action: (() -> Unit)? = null, +) { + val snackbar = Snackbar + .make(this, text, Snackbar.LENGTH_LONG) + .setTextColor(WHITE) + + if (action != null && actionText != null) { + snackbar.setAction(actionText) { action.invoke() } + } + if (duration != null) { + snackbar.duration = duration + } + + snackbar.view.setBackgroundColor(Color(0xFFE0244D).toArgb()); + snackbar.show() +} diff --git a/app/src/main/java/org/zotero/android/screens/login/LoginActivity.kt b/app/src/main/java/org/zotero/android/screens/login/LoginActivity.kt index e3929bc9..98745aac 100644 --- a/app/src/main/java/org/zotero/android/screens/login/LoginActivity.kt +++ b/app/src/main/java/org/zotero/android/screens/login/LoginActivity.kt @@ -2,29 +2,51 @@ package org.zotero.android.screens.login import android.content.Context import android.content.Intent +import android.net.Uri import android.os.Bundle -import androidx.activity.compose.setContent +import android.view.View +import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint +import org.zotero.android.androidx.content.longErrorSnackbar +import org.zotero.android.androidx.content.showKeyboard import org.zotero.android.architecture.BaseActivity +import org.zotero.android.architecture.Screen +import org.zotero.android.databinding.LoginActivityBinding import org.zotero.android.screens.dashboard.DashboardActivity -import org.zotero.android.uicomponents.theme.CustomTheme +import org.zotero.android.uicomponents.Strings +import org.zotero.android.uicomponents.snackbar.SnackbarMessage @AndroidEntryPoint -internal class LoginActivity : BaseActivity() { +internal class LoginActivity : BaseActivity(), Screen { + + private lateinit var binding: LoginActivityBinding + + private val viewModel: LoginViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { - CustomTheme { - LoginScreen( - onBack = { finish() }, - navigateToDashboard = { - startActivity(DashboardActivity.getIntentClearTask(this)) - } - ) - } + binding = LoginActivityBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + viewModel.observeViewChanges(this) + + binding.signInButton.setOnClickListener { + viewModel.onUsernameChanged(binding.usernameEditText.text.toString()) + viewModel.onPasswordChanged(binding.passwordEditText.text.toString()) + viewModel.onSignInClicked() } + binding.cancelButton.setOnClickListener { + finish() + } + binding.forgotPasswordButton.setOnClickListener { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://www.zotero.org/user/lostpassword?app=1") + ) + startActivity(intent) + } + showKeyboard(binding.usernameEditText) } companion object { @@ -35,5 +57,27 @@ internal class LoginActivity : BaseActivity() { } } } + + override fun render(state: LoginViewState) { + val snackbarMessage = state.snackbarMessage + if (snackbarMessage != null && snackbarMessage is SnackbarMessage.ErrorMessageString) { + binding.root.longErrorSnackbar(snackbarMessage.message) + viewModel.dismissSnackbar() + } + if (state.isLoading) { + binding.signInButton.text = "" + binding.progressIndicator.visibility = View.VISIBLE + } else { + binding.signInButton.text = getString(Strings.onboarding_sign_in) + binding.progressIndicator.visibility = View.GONE + } + } + + override fun trigger(effect: LoginViewEffect) = when (effect) { + LoginViewEffect.NavigateBack -> finish() + LoginViewEffect.NavigateToDashboard -> { + startActivity(DashboardActivity.getIntentClearTask(this)) + } + } } diff --git a/app/src/main/java/org/zotero/android/screens/login/LoginViewModel.kt b/app/src/main/java/org/zotero/android/screens/login/LoginViewModel.kt index abe2f4a8..d76aac94 100644 --- a/app/src/main/java/org/zotero/android/screens/login/LoginViewModel.kt +++ b/app/src/main/java/org/zotero/android/screens/login/LoginViewModel.kt @@ -94,7 +94,7 @@ internal class LoginViewModel @Inject constructor( } } - private fun dismissSnackbar() { + fun dismissSnackbar() { updateState { copy(snackbarMessage = null) } } diff --git a/app/src/main/res/drawable/round_corners_blue_button.xml b/app/src/main/res/drawable/round_corners_blue_button.xml new file mode 100644 index 00000000..b10dc946 --- /dev/null +++ b/app/src/main/res/drawable/round_corners_blue_button.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/login_activity.xml b/app/src/main/res/layout/login_activity.xml new file mode 100644 index 00000000..b4c0813a --- /dev/null +++ b/app/src/main/res/layout/login_activity.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index d4112ac1..5fbd6281 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -6,6 +6,7 @@ #4E4E4E @color/white + @color/black @color/dark_charcoal @color/charcoal @color/dark_theme_surface @@ -26,5 +27,6 @@ #1B1B1C #5B5B5E #282828 + #FF4072E5 #FF335BB8 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fc4b4f40..19e12644 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -16,7 +16,9 @@ #abacb7 #32333C #FF001F + #FF4072E5 #FF4071E6 + @color/white @color/cool_grey @color/white diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index da72ff34..50a85ccf 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -168,4 +168,24 @@ #9D9CA2 + + + + + diff --git a/buildSrc/src/main/kotlin/BuildConfig.kt b/buildSrc/src/main/kotlin/BuildConfig.kt index 587c9e0f..b8555839 100644 --- a/buildSrc/src/main/kotlin/BuildConfig.kt +++ b/buildSrc/src/main/kotlin/BuildConfig.kt @@ -4,7 +4,7 @@ object BuildConfig { const val compileSdkVersion = 34 const val targetSdk = 33 - val versionCode = 38 // Must be updated on every build + val versionCode = 39 // Must be updated on every build val version = Version( major = 1, minor = 0,