-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
1주차 과제 #3
1주차 과제 #3
Changes from 18 commits
2ed7755
678ccbb
2a6afb9
2228e67
f154dd9
eed5c4e
3ff2daf
2e16d3b
7425030
97ab0e7
68ee441
192bc21
6681825
d3f60e3
ebb9f2b
4950087
be1b533
5c2515a
00037f5
7ecebe3
e854340
f5bde1a
4cb3607
1cd9faf
bfc61b5
2e48b6f
d6b1089
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package org.sopt.dosopttemplate | ||
|
||
import android.app.Application | ||
import dagger.hilt.android.HiltAndroidApp | ||
|
||
@HiltAndroidApp | ||
class DoSoptApplication : Application() { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.sopt.dosopttemplate.data.repository | ||
|
||
import android.content.SharedPreferences | ||
import com.google.gson.Gson | ||
import com.google.gson.GsonBuilder | ||
import org.sopt.dosopttemplate.data.entity.User | ||
import org.sopt.dosopttemplate.domain.repository.SharedPrefRepository | ||
import javax.inject.Inject | ||
|
||
class SharedPrefRepositoryImpl @Inject constructor( | ||
private val sharedPref: SharedPreferences | ||
) : SharedPrefRepository { | ||
val gson: Gson = GsonBuilder().create() | ||
override fun saveUserInfo(user: User?) { | ||
val value = gson.toJson(user) | ||
sharedPref.edit().putString("user", value).apply() | ||
} | ||
|
||
override fun getUserInfo(): User? { | ||
val value = sharedPref.getString("user", "") | ||
if (value.isNullOrBlank()) return null | ||
return gson.fromJson(value, User::class.java) | ||
} | ||
|
||
override fun setAutoLogin() { | ||
sharedPref.edit().putBoolean("autoLogin", true).apply() | ||
} | ||
|
||
override fun isAutoLogin(): Boolean { | ||
return sharedPref.getBoolean("autoLogin", false) | ||
} | ||
|
||
override fun clearAutoLogin() { | ||
sharedPref.edit().putBoolean("autoLogin", false).apply() | ||
} | ||
|
||
override fun clearSharedPref() { | ||
sharedPref.edit().clear().apply() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.sopt.dosopttemplate.di | ||
|
||
import dagger.Binds | ||
import dagger.Module | ||
import dagger.hilt.InstallIn | ||
import dagger.hilt.components.SingletonComponent | ||
import org.sopt.dosopttemplate.data.repository.SharedPrefRepositoryImpl | ||
import org.sopt.dosopttemplate.domain.repository.SharedPrefRepository | ||
import javax.inject.Singleton | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
abstract class RepositoryModule { | ||
@Singleton | ||
@Binds | ||
abstract fun bindsSharedPrefRepository( | ||
sharedPrefRepository: SharedPrefRepositoryImpl | ||
): SharedPrefRepository | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. viewmodel에서 domain 레이어의 repository를 data의 구현부와 연결하여 사용하기위해 @BINDS 어노테이션을 활용했습니다 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.sopt.dosopttemplate.di | ||
|
||
import android.content.Context | ||
import android.content.SharedPreferences | ||
import dagger.Module | ||
import dagger.Provides | ||
import dagger.hilt.InstallIn | ||
import dagger.hilt.android.qualifiers.ApplicationContext | ||
import dagger.hilt.components.SingletonComponent | ||
import javax.inject.Singleton | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
object SharedPrefModule { | ||
@Provides | ||
@Singleton | ||
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences { | ||
return context.getSharedPreferences("prefs", Context.MODE_PRIVATE) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.sopt.dosopttemplate.domain.repository | ||
|
||
import org.sopt.dosopttemplate.data.entity.User | ||
|
||
interface SharedPrefRepository { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 진짜 mvvm 적용 폼 미쳤네,,, |
||
fun saveUserInfo(user: User?) | ||
fun getUserInfo(): User? | ||
fun setAutoLogin() | ||
fun isAutoLogin(): Boolean | ||
fun clearAutoLogin() | ||
fun clearSharedPref() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. domain layer이기 때문에 domain 의존성만을 갖고있어야 할 것 같습니다! domain 패키지의 User Model 을 생성 후 Data Layer 의 User를 mapping하는 것을 추천드립니다! |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -4,25 +4,32 @@ import android.content.Intent | |||||||||||||||||
import android.os.Bundle | ||||||||||||||||||
import androidx.activity.result.ActivityResultLauncher | ||||||||||||||||||
import androidx.activity.result.contract.ActivityResultContracts | ||||||||||||||||||
import androidx.activity.viewModels | ||||||||||||||||||
import dagger.hilt.android.AndroidEntryPoint | ||||||||||||||||||
import org.sopt.dosopttemplate.R | ||||||||||||||||||
import org.sopt.dosopttemplate.data.entity.User | ||||||||||||||||||
import org.sopt.dosopttemplate.databinding.ActivityLoginBinding | ||||||||||||||||||
import org.sopt.dosopttemplate.presentation.main.MainActivity | ||||||||||||||||||
import org.sopt.dosopttemplate.presentation.signup.SignupActivity | ||||||||||||||||||
import org.sopt.dosopttemplate.util.activity.hideKeyboard | ||||||||||||||||||
import org.sopt.dosopttemplate.util.binding.BindingActivity | ||||||||||||||||||
import org.sopt.dosopttemplate.util.context.toast | ||||||||||||||||||
import org.sopt.dosopttemplate.util.intent.getParcelable | ||||||||||||||||||
import org.sopt.dosopttemplate.util.view.UiState | ||||||||||||||||||
import org.sopt.dosopttemplate.util.view.snackBar | ||||||||||||||||||
|
||||||||||||||||||
@AndroidEntryPoint | ||||||||||||||||||
class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_login) { | ||||||||||||||||||
private lateinit var resultLauncher: ActivityResultLauncher<Intent> | ||||||||||||||||||
|
||||||||||||||||||
private val viewModel by viewModels<LoginViewModel>() | ||||||||||||||||||
override fun onCreate(savedInstanceState: Bundle?) { | ||||||||||||||||||
super.onCreate(savedInstanceState) | ||||||||||||||||||
|
||||||||||||||||||
binding.vm = viewModel | ||||||||||||||||||
initResultLauncher() | ||||||||||||||||||
initSignupButtonClickListener() | ||||||||||||||||||
initLoginButtonClickListener() | ||||||||||||||||||
initloginStateObserver() | ||||||||||||||||||
initHideKeyboard() | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private fun initResultLauncher() { | ||||||||||||||||||
|
@@ -31,7 +38,7 @@ class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_lo | |||||||||||||||||
) { result -> | ||||||||||||||||||
if (result.resultCode == RESULT_OK) { | ||||||||||||||||||
val user = result.data?.getParcelable(USER_KEY, User::class.java) | ||||||||||||||||||
|
||||||||||||||||||
viewModel.setAutoLogin(user) | ||||||||||||||||||
binding.root.snackBar { getString(R.string.login_success_signup) } | ||||||||||||||||||
setLoginButtonClickListener(user) | ||||||||||||||||||
} | ||||||||||||||||||
|
@@ -47,23 +54,41 @@ class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_lo | |||||||||||||||||
|
||||||||||||||||||
private fun initLoginButtonClickListener() { | ||||||||||||||||||
binding.btnLoginLogin.setOnClickListener { | ||||||||||||||||||
binding.root.snackBar { getString(R.string.login_fail_login) } | ||||||||||||||||||
viewModel.login() | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private fun setLoginButtonClickListener(user: User?) { | ||||||||||||||||||
binding.btnLoginLogin.setOnClickListener { | ||||||||||||||||||
if (isValidLogin(user)) { | ||||||||||||||||||
val intent = Intent(this, MainActivity::class.java) | ||||||||||||||||||
intent.putExtra(USER_KEY, user) | ||||||||||||||||||
this.toast(getString(R.string.login_success_login)) | ||||||||||||||||||
startActivity(intent) | ||||||||||||||||||
viewModel.login(user) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private fun initloginStateObserver() { | ||||||||||||||||||
viewModel.loginState.observe(this) { state -> | ||||||||||||||||||
when (state) { | ||||||||||||||||||
is UiState.Success -> { | ||||||||||||||||||
navigateToMainScreenWithUserData(state.data) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
is UiState.Empty -> {} | ||||||||||||||||||
else -> binding.root.snackBar { getString(R.string.login_fail_login) } | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private fun isValidLogin(user: User?): Boolean = | ||||||||||||||||||
binding.etLoginId.text.toString() == user?.id && binding.etLoginPw.text.toString() == user.pw | ||||||||||||||||||
private fun navigateToMainScreenWithUserData(user: User?){ | ||||||||||||||||||
val intent = Intent(this, MainActivity::class.java) | ||||||||||||||||||
intent.putExtra(USER_KEY, user) | ||||||||||||||||||
this.toast(getString(R.string.login_success_login)) | ||||||||||||||||||
viewModel.setAutoLogin(user) | ||||||||||||||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | ||||||||||||||||||
startActivity(intent) | ||||||||||||||||||
} | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. intent에 .apply{} 로 한번에 연결된 코드를 만들면 더 깔끔한 코드가 될 것 같습니다 !
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
private fun initHideKeyboard() { | ||||||||||||||||||
binding.root.setOnClickListener { hideKeyboard() } | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
companion object { | ||||||||||||||||||
private const val USER_KEY = "user" | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package org.sopt.dosopttemplate.presentation.login | ||
|
||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.MutableLiveData | ||
import androidx.lifecycle.ViewModel | ||
import dagger.hilt.android.lifecycle.HiltViewModel | ||
import org.sopt.dosopttemplate.data.entity.User | ||
import org.sopt.dosopttemplate.domain.repository.SharedPrefRepository | ||
import org.sopt.dosopttemplate.util.view.UiState | ||
import javax.inject.Inject | ||
|
||
@HiltViewModel | ||
class LoginViewModel @Inject constructor( | ||
val sharedPrefRepository: SharedPrefRepository | ||
) : ViewModel() { | ||
private val _loginState = MutableLiveData<UiState<User?>>(UiState.Empty) | ||
val loginState: LiveData<UiState<User?>> get() = _loginState | ||
|
||
val id = MutableLiveData<String>() | ||
val pw = MutableLiveData<String>() | ||
|
||
init { | ||
autoLogin() | ||
} | ||
Comment on lines
+22
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. init Block 활용 너무 좋아욥~!! |
||
|
||
private fun isValidLogin(user: User? = sharedPrefRepository.getUserInfo()): Boolean = | ||
id.value == user?.id && pw.value == user?.pw | ||
|
||
fun login(user: User? = null) { | ||
when (if (user == null) isValidLogin() else isValidLogin(user)) { | ||
true -> _loginState.value = UiState.Success(sharedPrefRepository.getUserInfo()) | ||
false -> _loginState.value = UiState.Failure(CODE_FAILURE) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 when 문이 신기하네요 이거 |
||
} | ||
|
||
private fun autoLogin() { | ||
if (sharedPrefRepository.isAutoLogin()) _loginState.value = | ||
UiState.Success(sharedPrefRepository.getUserInfo()) | ||
} | ||
|
||
fun setAutoLogin(user: User?) { | ||
sharedPrefRepository.saveUserInfo(user) | ||
sharedPrefRepository.setAutoLogin() | ||
} | ||
|
||
companion object { | ||
const val CODE_FAILURE = "fail" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gson도 좋지만 Kotlin serialization 이라는 라이브러리도 있습니다!
관련 글은 인간 안드로이드님의 블로그를 가져와봤어요 참고해보시면 좋을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 반영해보겠습니다 ㅎㅎ