Commit 7dc5d690 authored by jiangjiantao's avatar jiangjiantao

dev

parent d6fd8355
...@@ -30,6 +30,9 @@ android { ...@@ -30,6 +30,9 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
buildFeatures {
viewBinding true
}
} }
dependencies { dependencies {
...@@ -41,6 +44,7 @@ dependencies { ...@@ -41,6 +44,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.+' testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
......
...@@ -9,13 +9,16 @@ ...@@ -9,13 +9,16 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MiYaFastCashier"> android:theme="@style/Theme.MiYaFastCashier">
<activity android:name=".MainActivity"> <activity
android:name=".ui.login.ui.login.LoginActivity"
android:label="@string/title_activity_login">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".MainActivity"></activity>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
package com.miya.fastcashier.repository
class LoginRepository {
}
\ No newline at end of file
package com.miya.fastcashier.ui.login.ui.login
/**
* User details post authentication that is exposed to the UI
*/
data class LoggedInUserView(
val displayName: String
//... other data fields that may be accessible to the UI
)
\ No newline at end of file
package com.miya.fastcashier.ui.login.ui.login
import android.app.Activity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.Toast
import com.miya.fastcashier.R
import com.miya.fastcashier.databinding.ActivityLoginBinding
import com.miya.fastcashier.viewmodel.LoginViewModel
class LoginActivity : AppCompatActivity() {
private lateinit var loginViewModel: LoginViewModel
private lateinit var binding: ActivityLoginBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
val username = binding.username
val password = binding.password
val login = binding.login
val loading = binding.loading
// loginViewModel = ViewModelProvider(this, LoginViewModelFactory())
// .get(LoginViewModel::class.java)
//
// loginViewModel.loginFormState.observe(this@LoginActivity, Observer {
// val loginState = it ?: return@Observer
//
// // disable login button unless both username / password is valid
// login.isEnabled = loginState.isDataValid
//
// if (loginState.usernameError != null) {
// username.error = getString(loginState.usernameError)
// }
// if (loginState.passwordError != null) {
// password.error = getString(loginState.passwordError)
// }
// })
loginViewModel.loginResult.observe(this@LoginActivity, Observer {
val loginResult = it ?: return@Observer
loading.visibility = View.GONE
if (loginResult.error != null) {
showLoginFailed(loginResult.error)
}
if (loginResult.success != null) {
updateUiWithUser(loginResult.success)
}
setResult(Activity.RESULT_OK)
//Complete and destroy login activity once successful
finish()
})
username.afterTextChanged {
loginViewModel.loginDataChanged(
username.text.toString(),
password.text.toString()
)
}
password.apply {
afterTextChanged {
loginViewModel.loginDataChanged(
username.text.toString(),
password.text.toString()
)
}
setOnEditorActionListener { _, actionId, _ ->
when (actionId) {
EditorInfo.IME_ACTION_DONE ->
loginViewModel.login(
username.text.toString(),
password.text.toString()
)
}
false
}
login.setOnClickListener {
loading.visibility = View.VISIBLE
loginViewModel.login(username.text.toString(), password.text.toString())
}
}
}
private fun updateUiWithUser(model: LoggedInUserView) {
val welcome = getString(R.string.welcome)
val displayName = model.displayName
// TODO : initiate successful logged in experience
Toast.makeText(
applicationContext,
"$welcome $displayName",
Toast.LENGTH_LONG
).show()
}
private fun showLoginFailed(@StringRes errorString: Int) {
Toast.makeText(applicationContext, errorString, Toast.LENGTH_SHORT).show()
}
}
/**
* Extension function to simplify setting an afterTextChanged action to EditText components.
*/
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
}
\ No newline at end of file
package com.miya.fastcashier.ui.login.ui.login
/**
* Authentication result : success (user details) or error message.
*/
data class LoginResult(
val success: LoggedInUserView? = null,
val error: Int? = null
)
\ No newline at end of file
package com.miya.fastcashier.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import android.util.Patterns
import com.miya.fastcashier.R
import com.miya.fastcashier.repository.LoginRepository
import com.miya.fastcashier.ui.login.ui.login.LoggedInUserView
import com.miya.fastcashier.ui.login.ui.login.LoginResult
class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {
//
// private val _loginForm = MutableLiveData<LoginFormState>()
// val loginFormState: LiveData<LoginFormState> = _loginForm
//
// private val _loginResult = MutableLiveData<LoginResult>()
// val loginResult: LiveData<LoginResult> = _loginResult
//
// fun login(username: String, password: String) {
// // can be launched in a separate asynchronous job
//// val result = loginRepository.login(username, password)
////
//// if (result is Result.Success) {
//// _loginResult.value =
//// LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
//// } else {
//// _loginResult.value = LoginResult(error = R.string.login_failed)
//// }
// }
//
// fun loginDataChanged(username: String, password: String) {
// if (!isUserNameValid(username)) {
// _loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
// } else if (!isPasswordValid(password)) {
// _loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
// } else {
// _loginForm.value = LoginFormState(isDataValid = true)
// }
// }
//
// // A placeholder username validation check
// private fun isUserNameValid(username: String): Boolean {
// return if (username.contains('@')) {
// Patterns.EMAIL_ADDRESS.matcher(username).matches()
// } else {
// username.isNotBlank()
// }
// }
//
// // A placeholder password validation check
// private fun isPasswordValid(password: String): Boolean {
// return password.length > 5
// }
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".ui.login.ui.login.LoginActivity">
<EditText
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="96dp"
android:layout_marginEnd="24dp"
android:hint="@string/prompt_email"
android:inputType="textEmailAddress"
android:selectAllOnFocus="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:hint="@string/prompt_password"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:selectAllOnFocus="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="48dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="48dp"
android:layout_marginBottom="64dp"
android:enabled="false"
android:text="@string/action_sign_in"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password"
app:layout_constraintVertical_bias="0.2" />
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="32dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/password"
app:layout_constraintStart_toStartOf="@+id/password"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
\ No newline at end of file
<resources> <resources>
<string name="app_name">MiYaFastCashier</string> <string name="app_name">MiYaFastCashier</string>
<string name="title_activity_login">LoginActivity</string>
<string name="prompt_email">Email</string>
<string name="prompt_password">Password</string>
<string name="action_sign_in">Sign in or register</string>
<string name="action_sign_in_short">Sign in</string>
<string name="welcome">"Welcome !"</string>
<string name="invalid_username">Not a valid username</string>
<string name="invalid_password">Password must be >5 characters</string>
<string name="login_failed">"Login failed"</string>
</resources> </resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment