diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 49ec931..8d12516 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + + android:name=".ui.login.LoginActivity" + android:exported="true" + android:screenOrientation="portrait"> + + + + \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/MainActivity.kt b/app/src/main/java/org/android/go/sopt/MainActivity.kt deleted file mode 100644 index a2ef57d..0000000 --- a/app/src/main/java/org/android/go/sopt/MainActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.android.go.sopt - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/Week1Application.kt b/app/src/main/java/org/android/go/sopt/Week1Application.kt new file mode 100644 index 0000000..52d1a1c --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/Week1Application.kt @@ -0,0 +1,15 @@ +package org.android.go.sopt + +import android.app.Application +import org.android.go.sopt.util.PreferenceUtil + +class Week1Application: Application() { + override fun onCreate() { + prefs = PreferenceUtil(applicationContext) + super.onCreate() + } + + companion object { + lateinit var prefs: PreferenceUtil + } +} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/model/User.kt b/app/src/main/java/org/android/go/sopt/model/User.kt new file mode 100644 index 0000000..1c32ec2 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/model/User.kt @@ -0,0 +1,8 @@ +package org.android.go.sopt.model + +data class User( + val id: String, + val pw: String, + val name: String, + val hobby: String +) diff --git a/app/src/main/java/org/android/go/sopt/ui/login/LoginActivity.kt b/app/src/main/java/org/android/go/sopt/ui/login/LoginActivity.kt new file mode 100644 index 0000000..e7551e5 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/ui/login/LoginActivity.kt @@ -0,0 +1,126 @@ +package org.android.go.sopt.ui.login + +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import org.android.go.sopt.R +import org.android.go.sopt.Week1Application +import org.android.go.sopt.databinding.ActivityLoginBinding +import org.android.go.sopt.model.User +import org.android.go.sopt.ui.main.MainActivity +import org.android.go.sopt.ui.signup.SignUpActivity +import org.android.go.sopt.util.* +import org.android.go.sopt.util.extension.hideKeyboard +import org.android.go.sopt.util.extension.showSnackbar +import org.android.go.sopt.util.extension.showToast + +class LoginActivity : AppCompatActivity() { + private lateinit var binding: ActivityLoginBinding + private lateinit var userInfo: User + private var isUserRegistered: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityLoginBinding.inflate(layoutInflater) + setContentView(binding.root) + + handleAutoLogin() + initRootLayoutClickListener() + initSignUpButtonClickListener() + initLoginButtonClickListener() + } + + private fun handleAutoLogin() { + if (isLastUserLoggedIn()) { + navigateToMainScreen() + } + } + + private fun initLoginButtonClickListener() { + binding.btnLogin.setOnClickListener { + if (isUserRegistered) { + handleLoginResult() + } else { + showToast(getString(R.string.unregistered_msg)) + } + } + } + + private fun handleLoginResult() { + if (checkLoginInputValidity()) { + showToast(getString(R.string.login_success_msg)) + navigateToMainScreen() + } else { + showToast(getString(R.string.login_fail_msg)) + } + } + + private fun initSignUpButtonClickListener() { + val signUpResultLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == RESULT_OK) { + handleSignUpResult(result) + } + } + + binding.btnSignUp.setOnClickListener { + signUpResultLauncher.launch( + Intent(this, SignUpActivity::class.java) + ) + } + } + + private fun handleSignUpResult(result: ActivityResult) { + isUserRegistered = true + showSnackbar(binding.root, getString(R.string.sign_up_success_msg)) + + initUserInfoFromIntent(result.data) + saveUserInfoToPrefs() + } + + private fun initRootLayoutClickListener() { + binding.root.setOnClickListener { + hideKeyboard() + } + } + + private fun isLastUserLoggedIn(): Boolean { + val id = Week1Application.prefs.getString(ID_KEY, null) + val pw = Week1Application.prefs.getString(PW_KEY, null) + val name = Week1Application.prefs.getString(NAME_KEY, null) + val hobby = Week1Application.prefs.getString(HOBBY_KEY, null) + return id != null && pw != null && name != null && hobby != null + } + + private fun navigateToMainScreen() { + Intent(this, MainActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(this) + } + } + + private fun checkLoginInputValidity(): Boolean { + val id = binding.etId.text.toString() + val pw = binding.etPw.text.toString() + return userInfo.id == id && userInfo.pw == pw + } + + private fun saveUserInfoToPrefs() { + Week1Application.prefs.setString(ID_KEY, userInfo.id) + Week1Application.prefs.setString(PW_KEY, userInfo.pw) + Week1Application.prefs.setString(NAME_KEY, userInfo.name) + Week1Application.prefs.setString(HOBBY_KEY, userInfo.hobby) + } + + private fun initUserInfoFromIntent(intent: Intent?) { + val id = intent?.getStringExtra(ID_KEY).toString() + val pw = intent?.getStringExtra(PW_KEY).toString() + val name = intent?.getStringExtra(NAME_KEY).toString() + val hobby = intent?.getStringExtra(HOBBY_KEY).toString() + userInfo = User(id, pw, name, hobby) + } +} + diff --git a/app/src/main/java/org/android/go/sopt/ui/main/MainActivity.kt b/app/src/main/java/org/android/go/sopt/ui/main/MainActivity.kt new file mode 100644 index 0000000..c3b56db --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/ui/main/MainActivity.kt @@ -0,0 +1,23 @@ +package org.android.go.sopt.ui.main + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import org.android.go.sopt.Week1Application +import org.android.go.sopt.databinding.ActivityMainBinding +import org.android.go.sopt.util.HOBBY_KEY +import org.android.go.sopt.util.NAME_KEY + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + val name = Week1Application.prefs.getString(NAME_KEY, "nothing") + val hobby = Week1Application.prefs.getString(HOBBY_KEY, "nothing") + binding.tvName.append(name) + binding.tvHobby.append(hobby) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/ui/signup/SignUpActivity.kt b/app/src/main/java/org/android/go/sopt/ui/signup/SignUpActivity.kt new file mode 100644 index 0000000..0be6057 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/ui/signup/SignUpActivity.kt @@ -0,0 +1,119 @@ +package org.android.go.sopt.ui.signup + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.widget.addTextChangedListener +import org.android.go.sopt.R +import org.android.go.sopt.databinding.ActivitySignUpBinding +import org.android.go.sopt.model.User +import org.android.go.sopt.ui.login.LoginActivity +import org.android.go.sopt.util.* +import org.android.go.sopt.util.extension.hideKeyboard +import org.android.go.sopt.util.extension.showToast + +class SignUpActivity : AppCompatActivity() { + private lateinit var binding: ActivitySignUpBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySignUpBinding.inflate(layoutInflater) + setContentView(binding.root) + + initRootLayoutClickListener() + initEditTextChangedListeners() + initSignUpButtonClickListener() + } + + private fun initSignUpButtonClickListener() { + binding.btnSignUp.setOnClickListener { + val userInfo = getUserInfoInputValues() + if (checkSignUpInputValidity(userInfo)) { + sendUserInfoToLoginScreen(userInfo) + return@setOnClickListener + } + + showToast(getString(R.string.sign_up_invalid_input_err)) + } + } + + private fun sendUserInfoToLoginScreen(userInfo: User) { + Intent(this, LoginActivity::class.java).apply { + putExtra(ID_KEY, userInfo.id) + putExtra(PW_KEY, userInfo.pw) + putExtra(NAME_KEY, userInfo.name) + putExtra(HOBBY_KEY, userInfo.hobby) + setResult(RESULT_OK, this) + } + finish() + } + + private fun initEditTextChangedListeners() { + binding.etId.addTextChangedListener { + if (!checkLengthOfId(it.toString())) { + binding.tvIdLimitError.visibility = View.VISIBLE + } else { + binding.tvIdLimitError.visibility = View.INVISIBLE + } + } + + binding.etPw.addTextChangedListener { + if (!checkLengthOfPw(it.toString())) { + binding.tvPwLimitError.visibility = View.VISIBLE + } else { + binding.tvPwLimitError.visibility = View.INVISIBLE + } + } + + binding.etName.addTextChangedListener { + if (it.toString().isEmpty()) { + binding.tvNameEmptyError.visibility = View.VISIBLE + } else { + binding.tvNameEmptyError.visibility = View.INVISIBLE + } + } + + binding.etHobby.addTextChangedListener { + if (it.toString().isEmpty()) { + binding.tvHobbyEmptyError.visibility = View.VISIBLE + } else { + binding.tvHobbyEmptyError.visibility = View.INVISIBLE + } + } + } + + private fun initRootLayoutClickListener() { + binding.root.setOnClickListener { + hideKeyboard() + } + } + + private fun checkSignUpInputValidity(userInfo: User): Boolean { + return checkLengthOfId(userInfo.id) && checkLengthOfPw(userInfo.pw) && + userInfo.name.isNotEmpty() && userInfo.hobby.isNotEmpty() + } + + private fun getUserInfoInputValues(): User { + val id = binding.etId.text.toString() + val pw = binding.etPw.text.toString() + val name = binding.etName.text.toString() + val hobby = binding.etHobby.text.toString() + return User(id, pw, name, hobby) + } + + private fun checkLengthOfId(id: String): Boolean { + return id.length in ID_MIN_LEN..ID_MAX_LEN + } + + private fun checkLengthOfPw(pw: String): Boolean { + return pw.length in PW_MIN_LEN..PW_MAX_LEN + } + + companion object { + private const val ID_MIN_LEN = 6 + private const val ID_MAX_LEN = 10 + private const val PW_MIN_LEN = 8 + private const val PW_MAX_LEN = 12 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/util/Constants.kt b/app/src/main/java/org/android/go/sopt/util/Constants.kt new file mode 100644 index 0000000..0167e3d --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/util/Constants.kt @@ -0,0 +1,6 @@ +package org.android.go.sopt.util + +const val ID_KEY = "id" +const val PW_KEY = "pw" +const val NAME_KEY = "name" +const val HOBBY_KEY = "hobby" \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/util/PreferenceUtil.kt b/app/src/main/java/org/android/go/sopt/util/PreferenceUtil.kt new file mode 100644 index 0000000..0724810 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/util/PreferenceUtil.kt @@ -0,0 +1,21 @@ +package org.android.go.sopt.util + +import android.content.Context +import android.content.SharedPreferences + +class PreferenceUtil(context: Context) { + private val prefs: SharedPreferences = + context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) + + fun getString(key: String, defValue: String?): String? { + return prefs.getString(key, defValue) + } + + fun setString(key: String, str: String) { + prefs.edit().putString(key, str).apply() + } + + companion object { + private const val SHARED_PREFS_NAME = "my_prefs" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/android/go/sopt/util/extension/ActivityExt.kt b/app/src/main/java/org/android/go/sopt/util/extension/ActivityExt.kt new file mode 100644 index 0000000..6a5b019 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/util/extension/ActivityExt.kt @@ -0,0 +1,9 @@ +package org.android.go.sopt.util.extension + +import android.app.Activity +import android.view.View + +/** Hide keyboard from activity window */ +fun Activity.hideKeyboard() { + hideKeyboard(currentFocus ?: View(this)) +} diff --git a/app/src/main/java/org/android/go/sopt/util/extension/ContextExt.kt b/app/src/main/java/org/android/go/sopt/util/extension/ContextExt.kt new file mode 100644 index 0000000..30b2e49 --- /dev/null +++ b/app/src/main/java/org/android/go/sopt/util/extension/ContextExt.kt @@ -0,0 +1,24 @@ +package org.android.go.sopt.util.extension + +import android.app.Activity +import android.content.Context +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import com.google.android.material.snackbar.Snackbar + +/** Hide keyboard from window */ +fun Context.hideKeyboard(view: View) { + val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0) +} + +/** Make a Snackbar to display a message */ +fun Context.showSnackbar(view: View, msg: String) { + Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show() +} + +/** Make a Toast to display a message */ +fun Context.showToast(msg: String) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..ff75257 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + +