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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 17eab17..7115f25 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,15 +4,39 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".MainActivity">
+ tools:context=".ui.main.MainActivity">
+
+
+ app:layout_constraintTop_toBottomOf="@id/iv_profile" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_sign_up.xml b/app/src/main/res/layout/activity_sign_up.xml
new file mode 100644
index 0000000..b672fd9
--- /dev/null
+++ b/app/src/main/res/layout/activity_sign_up.xml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127..652ab33 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,5 @@
#FF018786
#FF000000
#FFFFFFFF
+ #F44336
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ef93038..e3b2f7e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,39 @@
GO SOPT Android
+
+
+ 회원가입을 먼저 진행해주세요.
+ 회원가입이 완료되었습니다.
+ 로그인에 성공했습니다.
+ 로그인에 실패했습니다.
+ 모든 항목에 유효한 값을 입력해주세요.
+
+
+ Welcome to GO Android!
+ ID
+ 아이디를 입력하세요.
+ PW
+ 비밀번호를 입력하세요.
+ Login
+ Sign Up
+
+
+ 이름:
+ 특기:
+
+
+ 회원가입
+ ID
+ * 6~10글자 이내만 허용
+ 아이디를 입력하세요.
+ PW
+ * 8~12글자 이내만 허용
+ 비밀번호를 입력하세요.
+ 이름
+ * 필수
+ 이름을 입력하세요.
+ 특기
+ * 필수
+ 특기를 입력하세요.
+ 회원가입 완료
\ No newline at end of file